http://codeforces.com/contest/1483
C. Skyline Photo
题意:给定 n 栋楼,每栋有高度 hi 且互不相同,还有魅力值 ai,要求分割成几块,每块的魅力值等于该块内最矮的楼的魅力值。要求最大化魅力值之和。
dp+线段树+单调栈
dp[i] 表示前 i 栋楼魅力值之和最大值。
枚举 i 所在块长度,则转移显然 dp[i]=j=1maxi(dp[j−1]+a[k]),其中 k 为 [j⋯i] 的中最矮的楼。
那么可以发现对于一些 j,他们对应的 k 可能是相同的,这样就可以放在一起求最大值。
维护递增的单调栈,则满足 stack[top] 是 [stack[top]⋯i] 中最矮的。
线段树维护 dp[j]+a[k],其中 k 为当前遍历到第 i 栋楼时,j 与 i 中间最矮的楼。
对于 i,加入单调栈后它与栈中前一个位置的楼中间所有的楼 j,对应的 k 都是 i,所以更新线段树。从单调栈中弹出时更新回去。求出 dp[i] 后更新到线段树上。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
| #include <bits/stdc++.h> #define debug(x) cout << #x << ":\t" << (x) << endl; using namespace std; #define ll long long #define ull unsigned ll const int N = 5e5 + 10; const int INF = 0x3f3f3f3f; const ll inf = 0x3f3f3f3f3f3f3f3f; const ll mod = 1e9 + 7; const ll inv2 = (mod + 1) / 2; int n; int h[N]; ll a[N], dp[N]; int st[N], tp; #define mid ((l+r)>>1) #define lson l,mid,rt<<1 #define rson mid+1,r,rt<<1|1 ll tr[N << 2], laz[N << 2]; void up(int rt) { tr[rt] = max(tr[rt << 1], tr[rt << 1 | 1]); } void down(int rt) { ll& x = laz[rt]; if (x) { tr[rt << 1] += x; tr[rt << 1 | 1] += x; laz[rt << 1] += x; laz[rt << 1 | 1] += x; x = 0; } } void build(int l, int r, int rt) { if (l == r) { if (l == 0)tr[rt] = 0; else tr[rt] = -inf; return; } build(lson); build(rson); up(rt); } void cg(int q, ll x, int l, int r, int rt) { if (l == r) { tr[rt] = x; return; } down(rt); if (q <= mid)cg(q, x, lson); else cg(q, x, rson); up(rt); } void upd(int ql, int qr, ll x, int l, int r, int rt) { if (ql <= l && qr >= r) { tr[rt] += x; laz[rt] += x; return; } down(rt); if (ql <= mid)upd(ql, qr, x, lson); if (qr > mid)upd(ql, qr, x, rson); up(rt); } int main() { scanf("%d", &n); for (int i = 1; i <= n; i++)scanf("%d", &h[i]); for (int i = 1; i <= n; i++)scanf("%lld", &a[i]); build(0, n, 1); st[tp++] = 0; for (int i = 1; i <= n; i++) { while (tp&&h[st[tp]] > h[i]) { upd(st[tp - 1], st[tp] - 1, -a[st[tp]], 0, n, 1); tp--; } st[++tp] = i; upd(st[tp - 1], st[tp] - 1, a[i], 0, n, 1); dp[i] = tr[1]; cg(i, dp[i], 0, n, 1); } printf("%lld\n", dp[n]); return 0; }
|
D. Useful Edges
题意:给定一张带边权无向图 2≤n≤600,0≤m≤2n(n−1),给定 q,1≤q≤2n(n−1) 个三元组 (u,v,l)。定义一条边为有用边当且仅当存在一个三元组 (u,v,l) 满足:存在从 u 到 v 的路径(可以不是简单路径),且该路径经过这条边,且该路径长度不超过 l。问有几条有用边。
Floyd最短路+枚举
说是最短路,但我感觉难点在枚举方法上。
对于一条边 (u,v),它有用等价于 存在一个三元组 (i,j,l[i][j]),满足 l[i][j]≥dist[u][j]+dist[v][i]+w[u][v]。
枚举 边的一个端点 u,以及三元组的一个端点 i,再枚举边的另一个端点 v,把上一行的式子移项,这样判断 (u,v) 是否有用就变成判断是否存在 j,使得 l[i][j]−dist[u][j]≥dist[v][i]+w[u][v],可以看到左边与 v 无关,右边与 j 无关,所以就可以分开算了,因此就变成判断 dist[v][i]+w[u][v]≤j=1maxn(l[i][j]−dist[u][j])。故只需要先枚举 j 计算出 j=1maxn(l[i][j]−dist[u][j]),再枚举 v 判断即可。
复杂度 O(n3)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| #include <bits/stdc++.h> #define debug(x) cout << #x << ":\t" << (x) << endl; using namespace std; #define ll long long #define ull unsigned ll const int N = 5e5 + 10; const int INF = 0x3f3f3f3f; const ll inf = 0x3f3f3f3f3f3f3f3f; const ll mod = 1e9 + 7; const ll inv2 = (mod + 1) / 2; int n, m, q; ll d[1010][1010], e[1010][1010], l[1010][1010];
int main() { scanf("%d%d", &n, &m); memset(e, -1, sizeof(e)); memset(d, 0x3f, sizeof(d)); for (int i = 1; i <= n; i++)d[i][i] = 0; for (int i = 1; i <= m; i++) { int u, v; ll w; scanf("%d%d%lld", &u, &v, &w); e[v][u] = e[u][v] = d[u][v] = d[v][u] = w; } for (int k = 1; k <= n; k++) { for (int i = 1; i <= n; i++) { for (int j = 1; j <= n; j++) { d[i][j] = min(d[i][j], d[i][k] + d[k][j]); } } } scanf("%d", &q); while (q--) { int u, v; ll w; scanf("%d%d%lld", &u, &v, &w); l[u][v] = l[v][u] = w; } int ans = 0; for (int i = 1; i <= n; i++) { for (int u = 1; u <= n; u++) { ll mx = -inf; for (int j = 1; j <= n; j++) { if (!l[i][j])continue; mx = max(mx, l[i][j] - d[u][j]); } for (int v = 1; v <= n; v++) { if (e[u][v] != -1 && d[i][v] + e[u][v] <= mx) { ans++; e[v][u] = e[u][v] = -1; } } } } printf("%d\n", ans); return 0; }
|