Loading

[题解][YZOJ50113] 枇杷树

简要题意

\(m\) 个操作,每次操作都会产生一个树的版本 \((\)\(0\) 开始\()\).

一次操作把 \(x_i\) 版本的树的点 \(u\)\(y_i\) 版本的树的点 \(v\) 连一条权值是 \(w\) 的边。\((\) \(y_i\) 上的全部点的编号加 \(siz_{x_i}\) \()\)

求每个版本:

\[\sum_{i = 1}^{siz - 1} \sum_{j = i + 1}^{siz} dis(i, j) \]

\(m\le 300,u,v\leq 10^{18}, siz \leq 2 \times 10 ^{18}\)

解题思路

首先,将答案考虑为每条边乘上两边的子树大小。

然后,考虑产生的新版本 (记为 \(z\) ) 的答案 \(ans_z\) 的组成:

  • 原来版本内的路径和:\(ans_x+ans_y\)
  • \((u,v,w)\) 的贡献:\(siz_x\times siz_y\times w\)
  • 版本间的贡献 ( 记 \(f_x(u)\) 为版本 \(x\) 中,所有点到 \(u\) 的路径和):\(f_x(u)\times siz_y+f_y(v)\times siz_x\).

那么现在的主要问题就是每次如何得到 \(f_x(u)\)。( 记 \(dis_x(u,v)\) 表示版本 \(x\) 中,\(u\)\(v\) 的路径长度)

还是分析当得到一个新的版本 \(z\) 时,\(f_z(a)\) 的组成:

  • \(a\) 原来属于版本 \(x\)\(f_x(a)+f_y(v)+(w+dis_x(a, u))\times siz_y\).
  • \(a\) 原来属于版本 \(y\)\(f_y(a)+f_x(u)+(w+dis_y(a, v))\times siz_x\).

此时,\(f_x(u)/f_y(v)\) 算作已知,故只需考虑 \(dis_z(a,b)\) 如何计算:

  • \(a,b\) 都在版本 \(x/y\),调用 \(dis_{x/y}(a,b)\);
  • \(a,b\) 不在同一版本,\(dis_x(a,u)+w+dis_y(b_v)\).

此时整个问题在递归中解决了,现在来分析一下复杂度。

对于 \(f_x(u)\),版本数是 \(O(m)\) 级别,\(u\) 的取值是 \(O(m)\) 级别,总的状态就是 \(O(m^2)\) 级别,转移 \(O(1)\)

\(dis_x(a, b)\) 的计算平均是 \(O(1)\) 的,所以计算所有的 \(f_x(u)\)\(O(m^2)\) 的。

计算 \(ans\) 时, \(f_x(u)\) 会被调用 \(O(m)\) 次,所以总的时间复杂度是 \(O(m^3)\)

代码

inline int Dis(int z, int a, int b){
	if(a == b) return 0;
	if(dis[z].find({a, b}) != dis[z].end()) return dis[z][{a, b}];
	int x = X[z], y = Y[z], w = W[z]; LL u = U[z], v = V[z];
	if(a <= siz[x] && b <= siz[x]) return dis[z][{a, b}] = Dis(x, a, b);
	if(siz[x] < a && siz[x] < b) return dis[z][{a, b}] = Dis(y, a - siz[x], b - siz[x]);
	int sum = w; if(a > siz[x]) swap(a, b);
	Plus(sum, Dis(x, a, u)), Plus(sum, Dis(y, b - siz[x], v));
	return dis[z][{a, b}] = sum;
}
inline int F(int z, LL a){
	if(z == 0 && a == 1) return 0;
	if(f[z].find(a) != f[z].end()) return f[z][a];
	int x = X[z], y = Y[z], w = W[z]; LL u = U[z], v = V[z];
	if(a <= siz[x]){
		int sum = 0;
		Plus(sum, F(x, a)), Plus(sum, F(y, v));
		Plus(sum, siz[y] % mod * Mod(w + Dis(x, a, u) - mod) % mod);
		return f[z][a] = sum;
	}
	int sum = 0;
	Plus(sum, F(y, a - siz[x])), Plus(sum, F(x, u));
	Plus(sum, siz[x] % mod * Mod(w + Dis(y, a - siz[x], v) - mod) % mod);
	return f[z][a] = sum;
}
inline void solve(int x, int y, LL u, LL v, int w, int z){
	X[z] = x, Y[z] = y, U[z] = u, V[z] = v, W[z] = w;
	Plus(Ans[z], Mod(Ans[x] + Ans[y] - mod));
	Plus(Ans[z], siz[x] % mod * (siz[y] % mod) % mod * w % mod);
	Plus(Ans[z], Mod(siz[y] % mod * F(x, u) % mod + siz[x] % mod * F(y, v) % mod - mod));
	siz[z] = siz[x] + siz[y]; 
}

参考

https://www.cnblogs.com/qjbqjb/p/15905279.html

posted @ 2022-03-29 22:01  IrisT  阅读(45)  评论(0编辑  收藏  举报