所谓树上启发式合并,就是在树上进行启发式合并
(逃)
解析
通过很妙的操作将暴力的复杂度优化到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_JUDGE
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
#endif
memset(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;
}