( ext{Description})
( ext{Solution})
好妙啊。
显然统计了 (u) 为根子树后要清空才能统计它的兄弟,我们尝试加一些小小的优化。
预处理出轻重儿子,由于如果一个儿子是最后遍历的就不用清空,直观地我们选择重儿子最后遍历。
如何证明时间复杂度?考虑一个点到根的链上最多只有 (log n) 条轻边。因为一条轻边意味着节点的父亲的 (siz) 大于此节点 (siz) 的两倍,所以一条轻边 (siz) 就会乘以 (2),最多只能乘 (log n) 次。
而一个节点只会被改到根的链上的轻边条数次(因为重儿子不被清空),时间复杂度 (mathcal O(nlog n))。
( ext{Code})
#include <cstdio>
#include <vector>
using namespace std;
typedef long long ll;
const int maxn=1e5+5;
int n,c[maxn],f[maxn],tot[maxn],siz[maxn],son[maxn],maxx;
ll ans[maxn],sum;
bool vis[maxn];
vector <int> e[maxn];
void dfs1(int u,int fa) {
f[u]=fa,siz[u]=1;
for(int i=0;i<e[u].size();++i) {
if(e[u][i]==fa) continue;
dfs1(e[u][i],u);
siz[u]+=siz[e[u][i]];
if(siz[e[u][i]]>siz[son[u]]) son[u]=e[u][i];
}
}
void calc(int u,int k) {
tot[c[u]]+=k;
if(tot[c[u]]>maxx) maxx=tot[c[u]],sum=c[u];
else if(tot[c[u]]==maxx) sum+=c[u];
for(int i=0;i<e[u].size();++i)
if(!vis[e[u][i]]&&e[u][i]!=f[u])
calc(e[u][i],k);
}
void dfs2(int u,int Keep) {
for(int i=0;i<e[u].size();++i) if(e[u][i]!=son[u]&&e[u][i]!=f[u]) dfs2(e[u][i],0);
if(son[u]) vis[son[u]]=1,dfs2(son[u],1);
calc(u,1); ans[u]=sum;
if(son[u]) vis[son[u]]=0;
if(!Keep) calc(u,-1),sum=maxx=0;
}
int main() {
scanf("%d",&n);
for(int i=1;i<=n;++i) scanf("%d",&c[i]);
for(int i=1;i<n;++i) {
int x,y;
scanf("%d %d",&x,&y);
e[x].push_back(y),e[y].push_back(x);
}
dfs1(1,0); dfs2(1,1);
for(int i=1;i<=n;++i) printf("%lld ",ans[i]); puts("");
return 0;
}