CSP-S2考前综合强化刷题 Day4

油箱

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 1m 最小值,可是万一等于枚举的 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+sr1,csl,c+gr,c=(fl,csl,c)+(gr,csr1,c)

最小。然后我们枚举 r r r 去找这个 l l l 就行了,那个减去的前缀和可以使它单调队列优化,我觉得比我想的麻烦。

数字

给出 n n n 个好数和 m m m 个坏数,求包含所有好数但不包含任何坏数,并且只由 1 ∼ 4 1 \sim 4 14 组成的数中,各位数字之和最小值是多少,数据保证有解。(好数和坏数均为 k k k 位数,且由 1 ∼ 4 1 \sim 4 14 组成)

看到这个数字很短了没,好数和坏数长度相同,那能不能建图?等等,你怎么想到建图的?就拿样例来模拟一下

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) 卡常。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值