复杂度证明
像树链剖分一样定义,则对于任何点轻儿子根到这点有
x
x
x条轻边,其子树大小为
y
y
y,轻儿子子树的大小
≤
\leq
≤父亲大小的一半,则
y
≤
n
2
x
y\leq \frac{n}{2^x}
y≤2xn,那么
x
≤
log
2
n
x\leq \log_2n
x≤log2n
如果一个节点是其父亲的重儿子,则它的子树必定大于它的兄弟,所以任何节点到根的路径上所有重边连接的父节点在计算答案时必定不会遍历到这个点。所以一个点被遍历的次数等于这个点到根节点路径中的轻边数
+
1
+1
+1。
总时间复杂度为
O
(
n
(
log
2
n
+
1
)
)
=
O
(
n
(
log
2
n
)
)
O(n(\log_2n+1))=O(n(\log_2n))
O(n(log2n+1))=O(n(log2n))
例题
#include <iostream>
#include <vector>
using namespace std;
typedef long long ll;
const int N = 1e5+10 ;
int c[N];
vector<int>g[N];
int sz[N],son[N];
void dfs1(int x,int f){
sz[x]=1;
for(auto &t:g[x]){
if(t==f)continue;
dfs1(t,x);
sz[x]+=sz[t];
if(sz[son[x]]<sz[t]){
son[x]=t;
}
}
}
ll cnt[N],ans[N],now;
ll sum=0,maxn=0;
void add(int x,int f,int u){
cnt[c[x]]+=u;
if(cnt[c[x]]>maxn){
sum=c[x];
maxn=cnt[c[x]];
}else if(cnt[c[x]]==maxn){
sum+=c[x];
}
for(auto &t:g[x]){
if(t==f||t==now)continue;
add(t,x,u);
}
}
void dfs2(int x,int f,bool flag){
for(auto &t:g[x]){
if(t==f||t==son[x])continue;
dfs2(t,x,false);
}
if(son[x]){
dfs2(son[x],x,true);
now=son[x];
}
add(x,f,1);
ans[x]=sum;
now=0;
if(!flag){
add(x,f,-1);
sum=maxn=0;
}
}
int main()
{
ios::sync_with_stdio(false);cin.tie(0),cout.tie(0);
int n;
cin>>n;
for(int i=1;i<=n;i++){
cin>>c[i];
}
for(int i=1;i<n;i++){
int a,b;
cin>>a>>b;
g[a].push_back(b);
g[b].push_back(a);
}
dfs1(1,0);
dfs2(1,0,0);
for(int i=1;i<=n;i++)
cout<<ans[i]<<" ";
}