题目大意
%
一棵树有
n
n
n 个结点,每个结点上有一种颜色,每个颜色有一个编号,求树中每个子树的最多的颜色编号的和。
数据范围
1
⩽
n
⩽
1
0
5
1\leqslant n\leqslant 10^5
1⩽n⩽105
题解
% 考虑dsu on tree的过程。
- 递归处理轻儿子。
- 递归处理重儿子。
- 暴力统计除了重儿子之外的子树信息,加到桶中。
- 累加答案。
- 如果当前节点 u u u 不是 u u u 的父节点的重儿子,删除这颗子树(包括 u u u 的重儿子)对桶的所有贡献,否则保留信息在桶中,直接返回。
%
这个过程对于一个已经明白算法运行逻辑的人来说已经很清楚了,但是对不不明白的人来说,仍然比较混乱,而且似乎找不到什么好的措辞来解释这部分内容,因此最好的办法是结合代码使用,下面的代码尽量写得每一行关键代码都有注释。
理解后我们来考虑这个过程的时间复杂度,对于一个点
u
u
u,若其到根节点的路径上有
k
k
k 条轻边,则这个点会被计算
k
k
k 次,由树链剖分的知识,我们可以得到,对于任意点
u
u
u,恒有
k
⩽
log
2
n
k\leqslant \log_2 n
k⩽log2n,因此总时间复杂度不超过
T
(
n
)
=
Θ
(
n
log
2
n
)
\text T(n)=\Theta(n\log_2 n)
T(n)=Θ(nlog2n)
#include<bits/stdc++.h>
using namespace std;
#define maxn 100010
struct edge{
int v,next;
}edges[maxn<<1];
int n,head[maxn];
void ins(int u,int v){
static int cnt=0;
edges[++cnt]=(edge){v,head[u]};
head[u]=cnt;
}
int size[maxn],son[maxn];
void dfs(int u,int fa=-1){
size[u]=1;
for(int i=head[u];i;i=edges[i].next){
int v=edges[i].v;
if(v==fa) continue;
dfs(v,u); size[u]+=size[v];
if(size[son[u]]<size[v]) son[u]=v;
}
}
int c[maxn];//节点的颜色
int cnt[maxn];//颜色的出现次数,即桶
int max_cnt;//出现次数最多的颜色的出现次数
int stop;//禁足点,用于标记重儿子
long long sum;//出现次数最多的颜色的编号和
void calc(int u,int fa,int val){
cnt[c[u]]+=val;//累加到桶重
if(max_cnt<cnt[c[u]]) max_cnt=cnt[c[u]],sum=c[u];//找到了出现次数更多的颜色,更新答案
else if(max_cnt==cnt[c[u]]) sum+=c[u];//又出现了一种出现次数一样多的颜色,累加答案
for(int i=head[u];i;i=edges[i].next){
int v=edges[i].v;
if(v==fa||v==stop) continue;//忽略禁足点和父节点
calc(v,u,val);//暴力递归统计答案
}
}
long long ans[maxn];
void work(int u,int fa,int chain){
for(int i=head[u];i;i=edges[i].next){
int v=edges[i].v;
if(v==fa||v==son[u]) continue;//先忽略中儿子和父亲
work(v,u,0);//递归处理轻儿子
}
if(son[u]){//考虑重儿子
work(son[u],u,1);//递归处理重儿子
stop=son[u];//标记重儿子不能在下面的calc函数中计算,因为在上一行的递归中,没有删除重儿子桶的贡献,已经被统计过了。
}
calc(u,fa,1);//暴力统计所有轻儿子的答案
stop=0;//这里删除对重儿子的禁足是为了下面50行的calc函数能把包括重儿子的整颗子树都删掉。
ans[u]=sum;//存下这个节点的答案——颜色编号和。
if(chain==0){//如果这是一条重链的开头,意味着其父亲和它的连边为轻边,要删除贡献
calc(u,fa,-1);//删除整颗子树的贡献
sum=max_cnt=0;//清空统计的信息
}
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&c[i]);
for(int i=1,u,v;i<n;i++){
scanf("%d%d",&u,&v);
ins(u,v);ins(v,u);
} dfs(1); work(1,-1,1);
for(int i=1;i<=n;i++)
printf("%I64d ",ans[i]);
return 0;
}