油箱
有 n n n 个城市,每个城市都有加油站,有 m m m 条单向道路,距离为 x x x 的道路需要消耗 x x x 升的汽油。请问你的车辆可以携带的最小油箱容量,使得不限加油次数的情况下,无论你在哪个城市都可以到达任意的城市。
二分答案,判断所有点在不在一个环上,可以直接 Tarjan,也可以正反建边从 1 1 1 号点出发看看能不能到达所有点,可惜我少了特判。
求和
年度最佳挂分,我的代码怎么被删了?
好吧,加上 ans =
就 AC 了。
这个题麻烦在于数对是有序的,所以你要分向上和向下考虑一下,我们树上前缀和只能维护到根的路径,所以向上的路径你考虑每个
a
i
a_i
ai 被算了几次,显然一个点被计算了它的根的路径上
b
i
b_i
bi 之和次,同样考虑向下的路径,那么考虑路径上的
b
i
b_i
bi 被算了几次,那么是到根的路径上的
a
i
a_i
ai 之和次。那么单独讨论一下一条链,对于一个路径拆成两条链分开求,在求它们之间的,这个就是一条链上的
a
i
a_i
ai 之和与另一条链上
b
i
b_i
bi 之和的乘积。然后减去多算的,先减去 lca 到根的路径中内部的答案,再减去 lca 到根的路径对两条链的贡献。
while (m--) {
int x = read(), y = read(), ans = 0;
int t = lca(x, y);
if (x == t) ans = down[y] - suma[fa[x][0]] * (sumb[y] - sumb[fa[x][0]]) - down[fa[x][0]];
else if (y == t) ans = up[x] - sumb[fa[y][0]] * (suma[x] - suma[fa[y][0]]) - up[fa[y][0]];
else ans = up[x] - sumb[fa[t][0]] * (suma[x] - suma[fa[t][0]]) - up[fa[t][0]] +
down[y] - suma[fa[t][0]] * (sumb[y] - sumb[fa[t][0]]) - down[fa[t][0]]
+ (suma[x] - suma[t]) * (sumb[y] - sumb[t]);
printf("%lld\n", ans);
}
染色
一排有 n n n 个格子,染成 m m m 种颜色,相邻格子颜色不能相同。此外,允许一个长度不超过 k k k 的区间染成相同的颜色。求最小代价。
这个暴力 dp 好写 f i , j , k f_{i,j,k} fi,j,k 表示前 i i i 位给第 i i i 位染了 j j j 色,还用没用机会的最小代价。然后我写了 2000 棵线段树优化成了 O ( n 2 log n ) O(n^2 \log n) O(n2logn) 和 O ( n 3 ) O(n^3) O(n3) 跑的一样快太妙了。
for (int i = 1; i <= n; i++) tt[i].build(1, 1, n);
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
f[i][j][0] = min(f[i][j][0], min(t0.query(1, 1, j - 1), t0.query(1, j + 1, m)) + cost[i][j]);
f[i][j][1] = min(f[i][j][1], min(t1.query(1, 1, j - 1), t1.query(1, j + 1, m)) + cost[i][j]);
f[i][j][1] = min(f[i][j][1], tt[j].query(1, min(1ll, i - lim + 1), i - 1) + cost[i][j]);
}
t0.build(1, 1, m, INF); t1.build(1, 1, m, INF);
for (int j = 1; j <= m; j++) {
t0.update(1, j, j, f[i][j][0]);
t1.update(1, j, j, f[i][j][1]);
tt[j].update(1, i, i, f[i][j][0]);
tt[j].update(1, 1, i - 1, cost[i][j]);
}
}
//暴力
if (k != j) {
f[i][j][0] = min(f[i][j][0], f[i - 1][k][0] + cost[i][j]);
f[i][j][1] = min(f[i][j][1], f[i - 1][k][1] + cost[i][j]);
}
for (int k = i - lim + 1; k < i; k++)
if (k >= 1) f[i][j][1] = min(f[i][j][1], f[k][j][0] + sum[i][j] - sum[k][j]);
好吧,你看看你转移的式子 ,后面那是一个区间的,它还加了一个前缀和,这一定是单调队列啊。那么上面那两坨怎么搞?其实你维护一个 1 ∼ m 1 \sim m 1∼m 最小值,可是万一等于枚举的 j j j 呢?就再维护一个次小值就搞定了 O ( n 2 ) O(n^2) O(n2)。
std 给了一个暴力,枚举相同颜色的区间和染的颜色,之后是一个 dp,这个 dp 相当于从中间切掉了一断区间,预处理一下前缀 dp 和后缀 dp 就可以 O ( 1 ) O(1) O(1) 更新了应该比较麻烦。现在枚举区间复杂度太慢了,直接枚举颜色 c c c 还是预处理线性 dp,那么我们要找到区间 [ l , r ] [l,r] [l,r] 使得
f l , c + s r − 1 , c − s l , c + g r , c = ( f l , c − s l , c ) + ( g r , c − s r − 1 , c ) f_{l,c} + s_{r - 1, c} - s_{l,c} + g_{r,c} \\= (f_{l,c} -s_{l,c}) + (g_{r,c}-s_{r-1,c}) fl,c+sr−1,c−sl,c+gr,c=(fl,c−sl,c)+(gr,c−sr−1,c)
最小。然后我们枚举 r r r 去找这个 l l l 就行了,那个减去的前缀和可以使它单调队列优化,我觉得比我想的麻烦。
数字
给出 n n n 个好数和 m m m 个坏数,求包含所有好数但不包含任何坏数,并且只由 1 ∼ 4 1 \sim 4 1∼4 组成的数中,各位数字之和最小值是多少,数据保证有解。(好数和坏数均为 k k k 位数,且由 1 ∼ 4 1 \sim 4 1∼4 组成)
看到这个数字很短了没,好数和坏数长度相同,那能不能建图?等等,你怎么想到建图的?就拿样例来模拟一下
3 3 4
1111
1222
2333
1122
1133
3122
12223331111 = 1222 + 2333 + 1111
那么我们要按一个顺序把好数全加进去,还不允许有坏数混进来,怎么办呢?先建出所有的不是坏点的点,如果一个点在后面加一个数,取后四位可以到另一个点,那么连边,代价是你加的数,你图的规模为 4 k 4^{k} 4k。那么问题转换成了经过 n n n 个点的哈曼顿通路,不如求出在原图中求一下两两的最短路,这是个 np 你状压一下。
时间复杂度 O ( n 2 × 2 n + 4 k × n ) O(n^2 \times 2^n + 4^k \times n) O(n2×2n+4k×n) 卡常。