做题记录

欢迎访问 Luogu 地址:Here


CF1201C

给定若干个数。可以进行 n 次单点加操作,求中位数的最大值。

二分答案,每次 O(n) check 即可。

CF1183C

多组询问。每一次查询给出四个数字:k,n,a,b

你在一个回合里面可以选择以下两种操作:

  • ka,并让最终答案 +1

  • kb,最终答案不变。

问在完成了 n 个回合之后,且 k>0 的前提下,最终答案的最大值。

可以直接数学题解个不等式,但是我沙比写了二分。

二分 a 的次数,剩下减 b 的次数 nmidcheck 一下是不是小于 0 就行了。

AC code

CF920G

给定 x,p,k。求 >x 的第 k 小的与 p 互质的数。

首先,发现答案具有可二分性。二分答案,发现问题转化为:求 i=1n[(i,p)=1]

对这个式子进行莫反变形:

i=1n[(i,p)=1]

=i=1nd|(i,p)μ(d)

=d|pμ(d)i=1nd1

=d|pμ(d)nd

筛出 μ 之后这个东西可以直接 O(d(p)) 的复杂度算。

p 的约数需要提前预处理出来。否则会在 #5 超时。

时间复杂度 O(max{p}+Td(p)logV)

CF140C

给定 n 个半径为 ri 的球。要求选出至多的三元组,使得三元组中的球半径两两不等。

考虑贪心。每个球按照出现次数排序扔到优先队列里去。每次取出出现次数最多的三个凑成一个三元组即可。时间复杂度 O(nlogn)

AC code

CF898E

给定 n 个数,每次操作可以加一或者减一。求将序列变成 n2 个完全*方数,剩下的都是非完全*方数的最小操作次数。

思路:找到每个值变成临*完全*方数的最小代价。然后根据代价排序,将前一半变成完全*方数。剩下变成非完全*方数,只需要加一即可。(当 ai=0 时要加二)。

AC code

CF888C

求最大的 k 值,使得存在一个字符 c 在文本串 s 的每个长度为 k 的子串中都出现过。

二分答案,设答案为 k,用类似双指针的思想跑一遍文本串进行 check。时间复杂度 O(nSlogn),其中 S 表示字符集大小。

AC code

P1730 最小密度路径

题解全是 n3m floyd 科技。但是分数规划显然更好想。

二分答案 d,检验的时候把所有边权减去 d,再看看 st 的路径长度是否小于零。

据说有向无环图 spfa 不好卡。所以复杂度 O(n2(n+m)logVϵ)O(1)

AC code

P1841 [JSOI2007] 重要的城市

显然,一个点是重要的城市,当且仅当存在 a,b,ab,从 ab 的最短路全部经过这个点。所以理所应当 a,b 统计从 ab 的最短路方案数。设为 fa,b。如果对于点 c,存在 fa,c×fc,b=fa,b,并且 acb 恰好是最短路,则 c 为重要城市。

由于点数很少,可以直接用 floyd

AC code

P1989 无向图三元环计数

喵喵题。

摒弃根号做法,世界属于 bitset。

考虑暴力做法:枚举每条边,端点 u,v,找到 w,使得 w,u,w,vEw 贡献为 1。复杂度 O(nm)。这个过程可以直接用 bitset 优化。也就是记一下 u,v 出边的点集,按位与一下。

但是空间开不下。可以参考三维偏序里面 bitset 的做法,直接按度分块。空间瞬间压缩成了 O(nmw)。轻松跑过。

rep(i, 1, n) {
	t &= 0;
	for (int j : E[i]) t.set(j);
	if (d[i] >= B) bit[id[i] = ++ cnt] = t;
	for (int j : E[i]) if (j < i) {
		if (id[j]) ans += (bit[id[j]] & t).count();
		else for (int k : E[j]) ans += t[k];
	}
}

P2169 正则表达式

懒得写代码了。简单题。

缩点。然后跑最短路。完了。

算了水蓝还是写写吧。

AC code

P3907 圈的异或

嘴巴一下。考虑拆位。第 k 次将边权重赋位 wi 的二进制第 k 位。然后把 0 的位缩点,把 1 的位保留。二分图染色即可。

如果不想写缩点啥的,其实可以直接 floyd

时间复杂度 O((n+m)logV)/O(n3logV)

提供一个 floyd check 部分的实现:

bool check(int w) {
	memset(f, 0, sizeof f);
	rep(i, 1, n) rep(j, 1, n) if (~g[i][j]) 
		f[i][j][(g[i][j] >> w) & 1] = 1;
	rep(k, 1, n) rep(i, 1, n) rep(j, 1, n)
		if ((i ^ j) && (j ^ k) && (i ^ k)) {
			f[i][j][0] |= (f[i][k][0] & f[k][j][0]);
			f[i][j][0] |= (f[i][k][1] & f[k][j][1]);
			f[i][j][1] |= (f[i][k][1] & f[k][j][0]);
			f[i][j][1] |= (f[i][k][0] & f[k][j][1]);
		}
	rep(i, 1, n) rep(j, 1, n) if (i ^ j) {
		if (f[i][j][0] && f[j][i][1]) return true;
		if (f[i][j][1] && f[j][i][0]) return true;
	} return false;
}

P2245 星际导航

简单题。典题。

求出原图的最小生成树,询问即为路径最大值。

可以直接倍增维护出来。注意询问两点不在同一颗树里的情况。注意森林需要每个根 dfs 一遍。

void init() {
	rep(j, 1, 20) rep(i, 1, n)
		fa[i][j] = fa[fa[i][j - 1]][j - 1];
	rep(j, 1, 20) rep(i, 1, n)
		f[i][j] = max(f[i][j - 1], f[fa[i][j - 1]][j - 1]);
}
int query(int u, int v) {
	int ans = 0;
	if (dep[u] < dep[v]) swap(u, v);
	dep(i, 20, 0) if (dep[fa[u][i]] >= dep[v]) 
		ans = max(ans, f[u][i]), u = fa[u][i];
	if (u == v) return ans;
	dep(i, 20, 0) if (fa[u][i] != fa[v][i])
		ans = max(ans, f[u][i]),
		ans = max(ans, f[v][i]),
		u = fa[u][i], v = fa[v][i];
	ans = max(ans, f[u][0]);
	ans = max(ans, f[v][0]);
	return ans;
}

P4665 [BalticOI 2015]

喵喵构造题。

可以发现连叶子节点最优。然后按照 dfs 序,将 ii+n2 两个叶子连起来行了。如果 n 是奇数,最后一个叶子随便挂上一个。

P2632 Explorer

挖个坑。感觉可以人类智慧。

比如随机旋转,飞快卡过之类的/cy

/cy

P5505 [JSOI2011] 分特产

容斥喵喵题。考虑让至少 i 个人没有拿到特产的方案数,使用插板法即为

(ak+ni1ni1)×(ni)

然后容斥一下就好了。

注意模数是 109+7 不是 998244353 [流汗黄豆]

rep(i, 0, n) {
	int cnt = 1;
	rep(j, 1, m)
		cnt = cnt * C(a[j] + n - i - 1, n - i - 1) % mod;
	ans = ans + ((i & 1 ? -1 : 1) * C(n, i) * cnt % mod);
	ans %= mod; ans += mod; ans %= mod;
} printf("%lld\n", ans); return 0;

CF160D Edges in MST

比较典的题。求无向图中那些边可能是 / 一定不是 / 一定是最小生成树中的边。

可以先拎出一颗最小生成树,然后枚举非树边 u,v。如果路径 (u,v) 中的最大值比 wu,v 小,则 u,v 一定不在最小生成树中。否则路径 (u,v) 中所有权值等于 wu,v 的点都可能是最小生成树中的边。

考虑标记这些边。一种显而易见的方法是直接上线段树合并。

AC code

CF1096E The Top Scorer

加训数学。题意大概如下:有 p 个人考试,已知他们的总分为 s。现在知道一号选手得分 r。求一号选手能够获得第一名的概率。特别的,如果有多个人分数相等且最高,则他们获得第一名的概率相等。

考虑枚举一号选手的成绩 x[r,s]。然后枚举与一号选手成绩相等的人数 i(包括一号选手本身)。剩下选手的总成绩 si×x,个数 pi。设 calc(n,s,k) 表示 n 个选手,总成绩为 s,最高成绩 <k 的方案数。则答案为 x=rsi=1p(p1i1)×calc(pi,six,x)

接下来考虑 calc 怎么搞。首先比较 naive 的是 n 个人总和为 s 且每个人都大于等于 0 的方案数,根据插板法就是 (s+n1n1)

那么正难则反,可以直接容斥一下。假设至少 i 个人分数 k,容斥系数为 (1)i(ni)。方案数是 (ni×k+n1n1)。这个方案数可以理解成,把 >k 的都削掉了 k,剩下的就无限制了。

然后注意没有意义的组合数不能计算贡献。时间复杂度 O(sp2)

int calc(int n, int s, int k) {
	if (n == 0) return s == 0;
	int ans = 0;
	rep(i, 0, n) {
		if (i * k > s) break;
		int u = s - i * k + n - 1;
		int v = n - 1, S = C(n, i) * C(u, v) % mod;
		ans += (i & 1 ? -1 : 1) * S % mod;
		ans = (ans % mod + mod) % mod;
	} return ans;
}
signed main() {
	read(p, s, r); fac[0] = inv[0] = 1;
	rep(i, 1, 6000) fac[i] = fac[i - 1] * i % mod;
	inv[6000] = qpow(fac[6000]);
	dep(i, 5999, 1) inv[i] = inv[i + 1] * (i + 1) % mod;
	rep(x, r, s) {
		rep(i, 1, p) {
			if (i * x > s) continue;
			int S = C(p - 1, i - 1) * calc(p - i, s - x * i, x) % mod;
			ans = ans + S * qpow(i) % mod; ans %= mod;
		}
	} int B = C(s - r + p - 1, p - 1);
	printf("%lld\n", ans * qpow(B) % mod);
	return 0;
}

CF838D Airplane Arrangements

CF2700,好仙的题。不看题解死也想不到。

首先转化成 n+1 长度的环,每个点随机起点和方向,不能走到 n+1 号点。

由于这是一个环,每个点没被占据的概率是相同的,即为 n+1mn+1。因此答案就是概率乘以总方案数即为 n+1mn+1×(2m(n+1)m)。再解释一下,2m 表示方向,(n+1)m 表示第 i 个人初始点的位置。

int p = (n - m + 1) * qpow(n + 1) % mod;
int b = qpow(2, m) * qpow(n + 1, m) % mod;
write('\n', p * b % mod); return 0;

CF997C

推了个式子出来好像错了。但是是线性的。先搁这放着。

设至少 ij 列染成了相同颜色。然后考虑容斥。

  • i0。那么方案数为

i=1n(1)i(ni)3i3n(ni)

  • j0,其方案数与 i=0 相同。

  • i,j 均不为 0。方案数为:

i=1nj=1n(1)i+j+1(ni)(nj)3×3(ni)(nj)

然后发现这个大组合可以拆成两半,而且两半还对称。所以就 O(n) 了。

然鹅似乎并不对。不管了。

update on 2023-12-03:

找到锅了。xab=(xa)b 而不是 xaxb。wssb。

第二个式子可以化成:

(3)×i=1nj=1n(1)i+j(ni)(nj)3(ni)(nj)

考虑右边的一大坨:

i=1nj=1n(1)i+j(ni)(nj)3(ni)(nj)

=i=1n(1)i(ni)j=1n(1)j(nj)3(ni)(nj)

w=3ni。原式变成:

i=1n(1)i(ni)j=1n(nj)(1)jwnj

抠出右边的式子,二项式定理逆用一下可以发现:

j=1n(nj)(1)jwnj=(w1)nwn

所以原式就是:

i=1n(1)i(ni)((w1)nwn)

复杂度 O(nlogn),瓶颈在快速幂。

真的,这道题让我对组合数学的理解加深了很多。

signed main() {
	// init combinations
	read(n); fac[0] = inv[0] = 1;
	rep(i, 1, n) fac[i] = fac[i - 1] * i % mod; inv[n] = qpow(fac[n]);
	dep(i, n - 1, 1) inv[i] = inv[i + 1] * (i + 1) % mod;
	// the first part
	rep(i, 1, n) {
		int s = qpow(3, i + n * (n - i)) * C(n, i) % mod;
		ans += (i & 1 ? 1 : -1) * s; ans = (ans % mod + mod) % mod;
	} ans = ans * 2; 
	// the second part
	int res = 0;
	rep(i, 1, n) {
		int w = qpow(3, n - i);
		int b = qpow((w - 1 + mod) % mod, n);
		b = b - qpow(w, n); b = (b + mod) % mod;
		int s = C(n, i) * b % mod;
		res += (i & 1 ? -1 : 1) * s;
		res = (res % mod + mod) % mod;
	} res = res * (-3) % mod;
	res = (res + mod) % mod;
	write('\n', (ans + res) % mod);
}

CF1097D Makoto and a Blackboard

题意:给定正整数 nk 次操作,每次操作把 n 等概率变为其约数。求最终 n 的期望。

喵喵题,还没写,但是做法能胡一下。

  • k104

首先考虑将 n 拆成质因数分解形式 n=i=1mpici。发现对于每个质因子答案相对独立。所以可以对每个质因子单独求答案。

fi,j 表示操作了 i 次质因子 p 的次数变为 j 的概率的期望。转移显然是:

fi,j=k=jc1k+1fi1,k

复杂度 klogn 完成转移。然后对于 p 这个质因子的答案 gp=fk,i×pi

最后合并答案,这个东西显然是积性函数,所以答案就是 ans=gpi

bonus:虽然对于每个质数求 fO(logn) 的,n 的质因子个数也是 O(logn) 级别的,看起来似乎是 log3n 状物,但是复杂度却是严格小于双 log 的。

  • k1018

f 可以矩阵转移。构造矩阵大小是 O(logn)×O(logn) 级别的。复杂度上界搞了搞,发现应该是 log103n 级别的东西(这里默认了 n,k 同阶)。比较牛逼。

神鱼写了看不懂的东西。当然我并不会。

头一次十分钟之内写完 CF2000+ 的题。贴一下 k104 的朴素实现。

int calc(int p, int c) {
	rep(i, 0, c) f[0][i] = f[1][i] = 0; f[0][c] = 1;
	rep(i, 1, k) rep(j, 0, c) {
		f[i & 1][j] = 0; rep(w, j, c) 
			f[i & 1][j] += f[(i & 1) ^ 1][w] * qpow(w + 1) % mod,
			f[i & 1][j] %= mod;
	} int s = 0;
	rep(i, 0, c) s = (s + qpow(p, i) * f[k & 1][i] % mod) % mod;
	return s;
}
signed main() {
	read(n, k);
	rep(i, 2, n / i) if (n % i == 0) {
		int cnt = 0; while (n % i == 0) cnt ++ , n /= i;
		ans = ans * calc(i, cnt) % mod;
	} if (n != 1) ans = ans * calc(n, 1) % mod;
	write('\n', ans); return 0;
}

CF1264D1 Beautiful Bracket Sequence (easy version)

可以发现最后任何合法的括号序列都可以删成形如 (((...))) 的形式而最大深度不变(也即每个合法括号序列要想变成最大深度,一定要删成一段 ( 拼上一段 ) 的形式)。

因此可以 dp。设 fi,j 表示前 i 个字符有 j 个左括号的方案数,gi,j 表示 inj) 的方案数。O(n2) 转移即可。

f[0][0] = 1;
rep(i, 1, n) rep(j, 0, i) {
	if (s[i] == '(') (f[i][j] += f[i - 1][j - 1]) %= mod;
	if (s[i] == ')') (f[i][j] += f[i - 1][j]) %= mod;
	if (s[i] == '?') (f[i][j] += (j ? f[i - 1][j - 1] : 0) + f[i - 1][j]) %= mod;
}
g[n + 1][0] = 1;
dep(i, n, 1) rep(j, 0, n) {
	if (s[i] == ')') (g[i][j] += (j ? g[i + 1][j - 1] : 0)) %= mod;
	if (s[i] == '(') (g[i][j] += g[i + 1][j]) %= mod;
	if (s[i] == '?') (g[i][j] += (j ? g[i + 1][j - 1] : 0) + g[i + 1][j]) %= mod;
}
rop(i, 1, n) rep(j, 1, n)
	(ans += f[i][j] * g[i + 1][j] % mod * j % mod) %= mod;

bonus:本题的组合做法:

枚举最终左右括号的分界位置。设 [1,i] 中有 s1(s2?[i+1,n] 中有 s3)s4?。那么对于这个分界点,方案数就是:

j(s2js1)(s4js3)j

其中 j 表示枚举的左右括号的数量,s1,s2,s3 可以前缀和。时间复杂度 O(n2)

rep(i, 1, n) s1[i] = s1[i - 1] + (s[i] == '(');
dep(i, n, 1) s2[i] = s2[i + 1] + (s[i] == ')');
rep(i, 1, n) s3[i] = s3[i - 1] + (s[i] == '?');
rep(i, 1, n) fac[i] = fac[i - 1] * i % mod; inv[n] = qpow(fac[n]);
dep(i, n - 1, 1) inv[i] = inv[i + 1] * (i + 1) % mod;

rep(i, 1, n) rep(j, 1, n) {
	int S1 = s1[i], S2 = s3[i];
	int S3 = s2[i + 1], S4 = s3[n] - s3[i];
	ans += C(S2, j - S1) * C(S4, j - S3) % mod * j; ans %= mod;
} write('\n', ans); return 0;

CF1264D2 Beautiful Bracket Sequence (hard version)

上一道题的加强版,也是第一道自己做出来的黑 / CF2900。

把上面一个题的组合式子变形一下即可。

rep(i, 1, n) {
	int S1 = s1[i], S2 = s3[i];
	int S3 = s2[i + 1], S4 = s3[n] - s3[i];
	ans += S1 * C(S2 + S4, S4 + S3 - S1) % mod; ans %= mod;
	ans += S2 * C(S2 + S4 - 1, S4 + S3 - S1 - 1) % mod; ans %= mod;
} write('\n', ans); return 0;

变形方法下午写。

update

原式为:

sum=j(s2js1)(s4js3)j

我们把 j 拆成 (js1)+s1。那么原式变成:

sum=j(s2js1)(s4js3)(s1+(js1))

=js1(s2js1)(s4js3)+j(js1)(s2js1)(s4js3)

=s1j(s2js1)(s4js3)+j(js1)(s2js1)(s4js3)

接下来用到一个组合恒等式:

(mk)(nrk)=(m+nr)(r=min(n,m))

因此前面一半式子就是:

s1j(s2js1)(s4js3)

=s1j(s2js1)(s4s4j+s3)

=s1(s2+s4s3+s4s1)

后面一半式子需要用到另外一个组合恒等式:

(nk)=nk(n1k1)

因此可以把第二个式子中的 j 约掉,得到:

j(js1)(s2js1)(s4js3)

=js2(s21js11)(s4js3)

=s2j(s2+s41s3+s4s11)

将两部分加起来就是答案。时间复杂度 O(n)

[ARC093F] Dark Horse

好难。不看题解想不到一点。

首先转化题目:将序列分成长度为 1,2,42n1 次方的 n 组,每组内的最小值都不属于 A

然后显然可以容斥。设状态 s 表示当前分组方案中,s1 的这些最小值属于 A。总方案就是 (2n)!s(1)|s|f(s)

然后就是不看题解死也想不到的 dp。

首先把 A 从大到小排序。

gi,j 表示当前扫到了 A 的第 i 位,当前分组中,已经分满并且最小值属于 A 的方案数。

转移比较*凡。考虑将 Ai 分进已经填满的组还是没有填满的组。

  • 分进填满的组,则有 gi,j=gi1,j

  • 分进未填满的组,则有 gi,j|2kgi1,j×(2njAi2k1)×(2k)!

最后,f(S)=gm,S×(2nS1)!

又一次被 dp 虐暴了。

int F(int s) {
	return f[m][s] * fac[tot - s - 1] % mod;
}
int count(int n, int s = 0) {
	while (n) s += (n & 1), n >>= 1; return s;
}
signed main() {
	read(n, m); tot = 1 << n; 
	fac[0] = inv[0] = 1; int lim = N - 5;
	rep(i, 1, lim) fac[i] = fac[i - 1] * i % mod; inv[lim] = qpow(fac[lim]);
	dep(i, lim - 1, 1) inv[i] = inv[i + 1] * (i + 1) % mod;
	rep(i, 1, m) read(a[i]);
	sort(a + 1, a + m + 1, greater<int>());
	f[0][0] = 1;
	rep(i, 1, m) rop(j, 0, tot) {
		(f[i][j] += f[i - 1][j]) %= mod;
		rop(k, 0, n) {
			if ((j >> k) & 1) continue;
			f[i][j | (1 << k)] += f[i - 1][j] * C(tot - j - a[i], (1 << k) - 1) % mod * fac[1 << k] % mod;
			f[i][j | (1 << k)] %= mod;
		}
	} rop(s, 0, tot) {
		ans += (count(s) & 1 ? -1 : 1) * F(s) % mod;
		ans = (ans % mod + mod) % mod;
	} ans = ans * tot % mod;
	write('\n', ans); return 0;
}

CF1536F

可以发现,无论怎么走都是后手必胜。

所以就不用考虑策略了。直接求不同走法的合法方案数即可。

可以发现,不可能有相邻的两个空格。因为如果有相邻的两个空格,只有可能是下面的情况:

A\_\_B

A\_\_A

B\_\_A

B\_\_B

这时候走子方一定能落子。

另外一个性质是,最后的局面除掉格子外一定是 ABABABAB 或者是 BABABABA 这样的格式。

因此考虑枚举当前进行了 2i 步,剩下了 n2i 个空格子。由于这是个环,所以分第一个是不是空格来计算。

假设第一个是空格,那么最后一个肯定不是空格。相当于在 2i+12 个空(减掉的二是首位两个)里面插入 2i1 个板(减掉的一个板子是开头的板)。方案数就是 (2i1n2i1)

假设第一个不是空格,那么方案数就是 (2in2i)

因此总方案数就是

2×i=1n2(2i)!((2i1n2i1)+(2in2i))

时间 O(n)。不知道为什么标签写了个中国剩余定理。

rep(i, 1, n >> 1) {
	int s = C(2 * i, n - 2 * i) + C(2 * i - 1, n - 2 * i - 1);
	ans = (ans + fac[2 * i] * s % mod) % mod;
} ans = 2 * ans % mod; write('\n', ans);

CF906D

一眼奶了。这不就欧拉降幂板子。

乍一看每次询问都得 O(n)。但是再想想,φ(φ(φ(n))) 的下降速度是非常快的,log 次就降成 1 了。所以直接暴力递归就可以了。时间复杂度显然 O(mlogp)

这就是 CF2700?

AC code

P4321 随机漫游

一道综合多种算法的好题。

首先按照图上随机游走的套路,再依据 n 很小的限制,可以设出 dp 方程:设 fs,u 表示当前走过的点集为二进制数 s,当前在 u 点,再走完所有点的期望步数。那么显然有 f(1<<n)1,u=1

然后写一下转移柿子:

fs,u(u,v)E1degufsv,v+1

然后发现这是一个 2n×n 元的线性方程组。所以可以直接高斯消元。时间复杂度 O((2n×n)3)。但是显然爆了。

再考虑将转移方程改写一下,分 v 原本是否属于 s 进行讨论:

fs,u1degu((u,v)E,vsfs,v+(u,v)E,vsfsv,v)+1

整理得到:

1degu(u,v)E,vsfs,vfs,u=(1degu(u,v)E,vsfsv,v+1)

这样,只需要按照集合 s 大小倒序枚举,方程组大小自然降为 O(n)

时间复杂度 O(2nn3+Q)

AC code

P3723 [AH2017/HNOI2017] 礼物

首先发现加减相对于两个手环是对称的。因此可以把对一个手环的减法转化成对另一个手环的加法。这样可以假设全是在第一个手环上执行的加减操作。

第一个手环执行了加 c 的操作,且旋转过之后的序列为 [x1,x2xn],第二个手环为 [y1,y2yn]。计算差异值并化简,可以得到差异值是:

x2+y2+c2n+2c(xy)2xy

可以发现,这个序列只有最后一项是不定的。

因此将 y 序列翻转后再复制一倍,与 x 卷积,答案就是卷积后序列的 n+12n 项系数的 max

直接暴力枚举 c,加上前面依托就行了。

AC code

CF633F The Chocolate Spree

点带权的树上选两条不交路径的最大权值和。

考虑做一个 dp。设 f(i,0/1/2/3/4) 表示考虑以 i 为根的子树,选了啥都没选 / 选了半条链(向上延伸) / 选了一条整链 / 选了一条整链 + 半条链 / 选了两条整链的最大权值。

贪心讨论转移。f(u,0)=0f(u,1)max{f(v,1)}+wuf(u,2)max{f(v,2)},max1{f(v,1)}+wu+max2{f(v,1)} 等等。

一发过了。我真是分类讨论大师。

void dfs(int u, int F) {
	vector<int> sons;
	for (auto v : E[u]) if (v ^ F)
		sons.push_back(v);
	for (auto v : sons) dfs(v, u);
	
	vector<PII> f1, f2, f3, f4;
	f1.push_back({0, 0});
	f2.push_back({0, 0});
	f3.push_back({0, 0});
	f4.push_back({0, 0});
	for (auto v : sons) {
		f1.push_back({f[v][1], v});
		f2.push_back({f[v][2], v});
		f3.push_back({f[v][3], v});
		f4.push_back({f[v][4], v});
	}
	sort(f1.begin(), f1.end());
	sort(f2.begin(), f2.end());
	sort(f3.begin(), f3.end());
	sort(f4.begin(), f4.end());
	reverse(f1.begin(), f1.end());
	reverse(f2.begin(), f2.end());
	reverse(f3.begin(), f3.end());
	reverse(f4.begin(), f4.end());
	
	f[u][0] = 0;
	f[u][1] = f1[0].x + w[u];
	f[u][2] = f2[0].x; 
	f[u][3] = f3[0].x + w[u]; 
	f[u][4] = f4[0].x;
	if (sons.size() >= 1)
		f[u][2] = max(f[u][2], w[u] + f1[0].x + f1[1].x);
	f[u][2] = max(f[u][2], f[u][1]);
	if (sons.size() == 0) return;
	for (auto v : sons)
		f[u][3] = max(f[u][3], f[v][2] + (f1[0].y == v ? f1[1].x : f1[0].x) + w[u]);
	f[u][4] = max(f[u][4], f2[0].x + f2[1].x);
	for (auto v : sons)
		f[u][4] = max(f[u][4], f[v][3] + w[u] + (f1[0].y == v ? f1[1].x : f1[0].x));
	if (sons.size() < 2) return;
	for (auto v : sons) {
		int a = 0, b = 1;
		if (f1[a].y == v) a += 2;
		if (f1[b].y == v) b ++ ;
		f[u][4] = max(f[u][4], f[v][2] + w[u] + f1[a].x + f1[b].x);
	} return;
}
posted @   Link-Cut-Y  阅读(41)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 记一次.NET内存居高不下排查解决与启示
点击右上角即可分享
微信分享提示