题目描述
有一棵 (n) 个结点的以 (1) 号结点为根的有根树。
每个结点都有一个颜色,颜色是以编号表示的, (i) 号结点的颜色编号为 (c_i)。
如果一种颜色在以 (x) 为根的子树内出现次数最多,称其在以 (x) 为根的子树中占主导地位。显然,同一子树中可能有多种颜色占主导地位。
你的任务是对于每一个 (iin[1,n]),求出以 (i) 为根的子树中,占主导地位的颜色的编号和。
(n≤10^5,c_ile n)
输入输出样例
输入 #1
4
1 2 3 4
1 2
2 3
2 4
输出 #1
10 9 3 4
输入 #2
15
1 2 3 1 2 3 3 1 1 3 2 2 1 2 3
1 2
1 3
1 4
1 14
1 15
2 5
2 6
2 7
3 8
3 9
3 10
4 11
4 12
4 13
输出 #2
6 5 4 3 2 3 3 1 1 3 2 2 1 2 3
分析
DSU ON TREE洛谷日报
对于这一道题,因为一个子树递归结束后产生的影响会传递给下一个子树
所以在每一次计算之前都要清空贡献
但是这样做时间复杂度吃不消
其实 (dfs) 时有一个性质
对于一个节点,它所递归的最后一个子树的价值是不用清空的
而我们要尽量使这个子树保留的信息更多
因此我们选择这个节点的重儿子进行递归
对于轻儿子暴力删除和加入
复杂度证明请看洛谷日报
代码
#include<cstdio>
#include<cstring>
inline int read(){
register int x=0,fh=1;
char ch=getchar();
while(ch<'0' || ch>'9'){
if(ch=='-') fh=-1;
ch=getchar();
}
while(ch>='0' && ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*fh;
}
const int maxn=2e5+5;
int h[maxn],tot=1;
struct asd{
int to,nxt;
}b[maxn];
void ad(int aa,int bb){
b[tot].to=bb;
b[tot].nxt=h[aa];
h[aa]=tot++;
}
int n,c[maxn],f[maxn],son[maxn],siz[maxn];
void dfs(int now,int fa){
siz[now]=1;
f[now]=fa;
for(register int i=h[now];i!=-1;i=b[i].nxt){
register int u=b[i].to;
if(u==fa) continue;
dfs(u,now);
siz[now]+=siz[u];
if(son[now]==0 || siz[son[now]]<siz[u]){
son[now]=u;
}
}
}
//求出每一个节点的重儿子
int cnt[maxn],orz;
long long sum,mmax,ans[maxn];
void xg(int now,int val){
cnt[c[now]]+=val;
if(cnt[c[now]]>mmax){
mmax=cnt[c[now]];
sum=c[now];
} else if(cnt[c[now]]==mmax){
sum+=c[now];
}
for(int i=h[now];i!=-1;i=b[i].nxt){
int u=b[i].to;
if(u==f[now] || u==orz) continue;
xg(u,val);
}
}
//删除/加入贡献
void dfs2(int now,int op){
for(register int i=h[now];i!=-1;i=b[i].nxt){
register int u=b[i].to;
if(u==f[now] || u==son[now]) continue;
dfs2(u,0);
}
if(son[now]){
dfs2(son[now],1);
orz=son[now];
}
xg(now,1);
orz=0;
ans[now]=sum;
if(op==0){
xg(now,-1);
sum=mmax=0;
}
}
//求出答案
int main(){
memset(h,-1,sizeof(h));
n=read();
for(register int i=1;i<=n;i++){
c[i]=read();
}
int aa,bb;
for(register int i=1;i<n;i++){
aa=read(),bb=read();
ad(aa,bb);
ad(bb,aa);
}
dfs(1,0);
dfs2(1,0);
for(register int i=1;i<=n;i++){
printf("%lld ",ans[i]);
}
printf("
");
return 0;
}