dsu
dsu是并查集的意思,但是dsu on tree和并查集没什么关系。主要是借助并查集的思想,在树上进行一个合并。
解决什么问题
树上的问题大体可分两种,子树问题和树链问题。
树链问题可以用树链剖分来做,dsu on tree主要是解决不修改的子树问题。
核心思想
将轻子树往重子树上合并,删除轻子树的贡献。
什么是轻子树和重子树
我们定义轻子树是轻儿子所在的子树,重子树是重儿子所在的子树。
具体做法
处理轻子树及其子树的答案
处理重子树及其子树的贡献,并不删除贡献
暴力统计所有轻子树及其子树的贡献与上一步统计的贡献合并,得到节点
u
u
u 的答案
删除轻子树及其子树的贡献
模板
void dfs1(int u, int f){ // 预处理重儿子
sz[u] = 1;
int maxx = 0;
for(int i = head[u]; ~i; i = e[i].next){
int v = e[i].to;
if(v == f) continue;
dfs1(v, u);
sz[u] += sz[v];
if(sz[v] > maxx){
maxx = sz[v];
son[u] = v;
}
}
}
int flag;
void count(int u, int f, int val){
// 处理该节点
for(int i = head[u]; ~i; i = e[i].next){
int v = e[i].to;
if(v == f || v == flag) continue;
count(v, u, val);
}
}
void dsu(int u, int f, int keep){ // keep 为false表示此子树为轻子树 要删除此子树的贡献
for(int i = head[u]; ~i; i = e[i].next){ // 先处理轻子树问题
int v = e[i].to;
if(v == f || v == son[u]) continue;
dsu(v, u, 0);
}
if(son[u]){ // 处理重子树问题
dsu(son[u], u, 1);
flag = son[u]; // 标记重子树 防止后面处理轻子树进入重子树分支
}
count(u, f, 1); // 统计轻子树的答案
flag = 0;
// 统计答案
if(!keep){ // 删除轻子树的贡献
count(u, f, -1);
}
}