[题解][YZOJ50113] 枇杷树
简要题意#
个操作,每次操作都会产生一个树的版本 从 开始.
一次操作把 版本的树的点 和 版本的树的点 连一条权值是 的边。 上的全部点的编号加
求每个版本:
解题思路#
首先,将答案考虑为每条边乘上两边的子树大小。
然后,考虑产生的新版本 (记为 ) 的答案 的组成:
- 原来版本内的路径和:;
- 边 的贡献:;
- 版本间的贡献 ( 记 为版本 中,所有点到 的路径和):.
那么现在的主要问题就是每次如何得到 。( 记 表示版本 中, 到 的路径长度)
还是分析当得到一个新的版本 时, 的组成:
- 若 原来属于版本 ,.
- 若 原来属于版本 ,.
此时, 算作已知,故只需考虑 如何计算:
- 都在版本 ,调用 ;
- 不在同一版本,.
此时整个问题在递归中解决了,现在来分析一下复杂度。
对于 ,版本数是 级别, 的取值是 级别,总的状态就是 级别,转移 。
的计算平均是 的,所以计算所有的 是 的。
计算 时, 会被调用 次,所以总的时间复杂度是 。
代码#
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];
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现