链接: http://codeforces.com/contest/600/problem/E
题意: 现在给你一棵树,n个节点,每个节点都有一个颜色,现在你要求出以u 为根节点的子树中出现最多的颜色和,既如果这颗子树中1出现了3次,3出现了3次,2出现了2次,4出现了1次,那么答案就是1+3=4.
思路: 如果最傻逼的暴力就是一个n方的算法。但是这里可以利用树剖 的轻重孩子在nlogn的时间内解决这个问题,也就是一个比较好的优化。
这里有个CF上大佬的博客。挺好的。
链接:http://codeforces.com/blog/entry/44351#comment-332425
就是讲的如何用Rsu on tree 将这个问题优化到nlogn的时间内。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N =1e5+5;
bool big[N];
int cnt[N];
int col[N];
int sz[N];
int ansc[N];
ll ans[N];
vector< int >ve[N];
int maxx;
ll sum;
int n;
void dfs1(int u,int fa)
{
sz[u]=1;
for(int i=0;i<ve[u].size();i++){
int v=ve[u][i];
if(v==fa) continue;
dfs1(v,u);
sz[u]+=sz[v];
}
}
void add(int u,int fa,int val)
{
cnt[col[u]]+=val;
if(cnt[col[u]]>maxx){
maxx=cnt[col[u]];
sum=1ll*col[u];
}
else if(cnt[col[u]]==maxx){
sum+=1ll*col[u];
}
for(int i=0;i<ve[u].size();i++){
int v=ve[u][i];
if(v==fa||big[v]) continue;
add(v,u,val);
}
}
void dfs(int u,int fa,int keep)
{
//cout<<"u "<<u<<" fa "<<fa<<" keep "<<keep<<endl;
int mx=-1,bigc=-1;
for(int i=0;i<ve[u].size();i++){
int v=ve[u][i];
if(v==fa) continue;
if(sz[v]>mx){
mx=sz[v];
bigc=v;
}
}
for(int i=0;i<ve[u].size();i++){
int v=ve[u][i];
if(v==fa||v==bigc) continue;
dfs(v,u,0);
}
if(bigc!=-1){
dfs(bigc,u,1);
big[bigc]=1;
}
if(bigc!=-1){
maxx=ansc[bigc];
sum=ans[bigc];
}
else maxx=sum=0;
add(u,fa,1);
ans[u]=sum;
ansc[u]=maxx;
if(bigc!=-1) big[bigc]=0;
if(keep==0) add(u,fa,-1);
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&col[i]);
int u,v;
for(int i=1;i<n;i++){
scanf("%d %d",&u,&v);
ve[u].push_back(v);
ve[v].push_back(u);
}
dfs1(1,1);
dfs(1,1,1);
for(int i=1;i<=n;i++) printf("%lld ",ans[i]);
return 0;
}