文章目录
- 解析
- step1:树剖
- step2:求出轻儿子的答案(不继承)
- step3:求出重儿子的答案(继承)
- step4:加入自己的答案、合并轻儿子的答案并记录答案
- step5:清空
- 复杂度分析
- 代码
所谓树上启发式合并,就是在树上进行启发式合并
(逃)
解析
通过很妙的操作将暴力的复杂度优化到log级别
可以解决对子树的一些离线静态问题
大概讲一下流程:
step1:树剖
先剖一下确定重儿子为后来的dsu做准备
顺便求出size和dfs序等信息
注意这个dfs序不一定要先搜重儿子
所以直接在一个dfs函数里解决就可以
void dfs0(int x,int f){siz[x]=1;L[x]=++tim;dfn[tim]=x;for(int i=fi[x];~i;i=p[i].nxt){int to=p[i].to;if(to==f) continue;dfs0(to,x);if(siz[hson[x]]<siz[to]) hson[x]=to;siz[x]+=siz[to];}R[x]=tim;
}
step2:求出轻儿子的答案(不继承)
接下来的操作都在dsu的主函数中:
void dfs(int x,int f,bool kep)
kep表示当前结点的信息是否需要传给父亲
先递归把所有轻儿子的答案求出来
不继承给父亲
for(int i=fi[x];~i;i=p[i].nxt){int to=p[i].to;if(to==f||to==hson[x]) continue;dfs(to,x,0);}
step3:求出重儿子的答案(继承)
求出重儿子的答案,并继承到父亲接着用
if(hson[x]) dfs(hson[x],x,1);
step4:加入自己的答案、合并轻儿子的答案并记录答案
把自己的答案加进来
并利用求好的dfs序
暴力把所有轻儿子的答案合并到父亲
最后记录一下当前的答案
add(x);for(int i=fi[x];~i;i=p[i].nxt){int to=p[i].to;if(to==f||to==hson[x]) continue;for(int j=L[to];j<=R[to];j++) add(dfn[j]);}ans[x]=res;
step5:清空
如果不需要传给父亲,就把整个子树的信息暴力清空
if(!kep){for(int i=L[x];i<=R[x];i++) del(dfn[i]);res=mx==0;}
复杂度分析
考虑一个结点被遍历到几次
注意到一个结点每作为一个轻儿子的子树,就会被多遍历一次
上面那个东西其实就是到根的轻链的数目
众所周知这玩意是log的
所以复杂度是非常优秀的nlogn
应该和树剖一样跑的很不满
当然如果单次合并答案复杂度不是O1那还要再乘合并复杂度
代码
题目传送门
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=1e5+100;
const int mod=998244353;
inline ll read() {ll x=0,f=1;char c=getchar();while(!isdigit(c)) {if(c=='-')f=-1;c=getchar();}while(isdigit(c)) {x=(x<<1)+(x<<3)+c-'0';c=getchar();}return x*f;
}
int n,m;
struct node{int to,nxt;
}p[N<<1];
int fi[N],cnt;
inline void addline(int x,int y){p[++cnt]=(node){y,fi[x]};fi[x]=cnt;return;
}
int siz[N],L[N],R[N],tim,hson[N],dfn[N];
int col[N],bac[N],mx;
ll ans[N],res;
void dfs0(int x,int f){siz[x]=1;L[x]=++tim;dfn[tim]=x;for(int i=fi[x];~i;i=p[i].nxt){int to=p[i].to;if(to==f) continue;dfs0(to,x);if(siz[hson[x]]<siz[to]) hson[x]=to;siz[x]+=siz[to];}R[x]=tim;
}
inline void add(int p){int o=col[p];++bac[o];if(bac[o]>mx){mx=bac[o];res=o;}else if(bac[o]==mx) res+=o;return;
}void dfs(int x,int f,bool kep){for(int i=fi[x];~i;i=p[i].nxt){int to=p[i].to;if(to==f||to==hson[x]) continue;dfs(to,x,0);}if(hson[x]) dfs(hson[x],x,1);add(x);for(int i=fi[x];~i;i=p[i].nxt){int to=p[i].to;if(to==f||to==hson[x]) continue;for(int j=L[to];j<=R[to];j++) add(dfn[j]);}ans[x]=res;if(!kep){for(int i=L[x];i<=R[x];i++) bac[col[dfn[i]]]=0;res=mx=0;}return;
}
int main(){#ifndef ONLINE_JUDGEfreopen("a.in","r",stdin);freopen("a.out","w",stdout);#endifmemset(fi,-1,sizeof(fi));cnt=-1;n=read();for(int i=1;i<=n;i++) col[i]=read();for(int i=1;i<n;i++){int x=read(),y=read();addline(x,y);addline(y,x);}dfs0(1,0);dfs(1,0,1);for(int i=1;i<=n;i++) printf("%lld ",ans[i]);return 0;
}