CodeForces

( 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;
} 
原文地址:https://www.cnblogs.com/AWhiteWall/p/14279964.html