本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作。
本文作者:ljh2000
作者博客:http://www.cnblogs.com/ljh2000-jump/
转载请注明出处,侵权必究,保留最终解释权!
题目链接:codeforces600E
解法一:$O(nlog^2n)$
正解:启发式合并
解题报告:
这道题求的是每个点的子树内的出现次数最大的数字的和。
考虑启发式合并,我用$col[x]$的$map$表示$x$的子树内的每种权值的出现次数,$sum[x]$的$map$表示$x$的子树内每种出现次数的权值和。
那么我在将儿子节点和父亲节点合并的时候只需要根据$col$的$size$,把小的往大的里面暴力合并就可以了。
$get$了$map$的正确姿势…
//It is made by ljh2000 //有志者,事竟成,破釜沉舟,百二秦关终属楚;苦心人,天不负,卧薪尝胆,三千越甲可吞吴。 #include <iostream> #include <cstdlib> #include <cstring> #include <cstdio> #include <cmath> #include <algorithm> #include <ctime> #include <vector> #include <queue> #include <map> #include <set> #include <string> #include <complex> #include <bitset> using namespace std; typedef long long LL; typedef long double LB; const double pi = acos(-1); const int MAXN = 100011; const int MAXM = 200011; int n,a[MAXN],ecnt,first[MAXN],to[MAXM],next[MAXM]; LL ans[MAXN]; map<int,int>col[MAXN];//统计每种颜色的出现次数 map<int,LL>sum[MAXN];//统计每种出现次数的sum inline void link(int x,int y){ next[++ecnt]=first[x]; first[x]=ecnt; to[ecnt]=y; } inline int getint(){ int w=0,q=0; char c=getchar(); while((c<'0'||c>'9') && c!='-') c=getchar(); if(c=='-') q=1,c=getchar(); while (c>='0'&&c<='9') w=w*10+c-'0',c=getchar(); return q?-w:w; } inline void dfs(int x,int fa){ col[x][a[x]]=1; sum[x][1]=a[x]; int now,cc; for(int i=first[x];i;i=next[i]) { int v=to[i]; if(v==fa) continue; dfs(v,x); if(col[x].size()<col[v].size()) swap(col[x],col[v]),swap(sum[x],sum[v]);//小的往大的上面合并!!! for(map<int,int>::iterator it=col[v].begin();it!=col[v].end();it++) { now=it->first; cc=it->second; if(col[x].count(now)>0) { sum[x][col[x][now]]-=now; col[x][now]+=cc; sum[x][col[x][now]]+=now; } else col[x][now]=col[v][now],sum[x][col[x][now]]+=now; } } map<int,LL>::iterator it=sum[x].end(); it--;//最大的应该是end-1,end是一个空指针... ans[x]=it->second; } inline void work(){ n=getint(); for(int i=1;i<=n;i++) a[i]=getint(); int x,y; for(int i=1;i<n;i++) { x=getint(); y=getint(); link(x,y); link(y,x); } dfs(1,0); for(int i=1;i<=n;i++) printf("%I64d ",ans[i]); } int main() { work(); return 0; } //有志者,事竟成,破釜沉舟,百二秦关终属楚;苦心人,天不负,卧薪尝胆,三千越甲可吞吴。
解法二:$O(nlogn)$
正解:$dsu$ $on$ $tree$
解题报告:
学了一发$dsu$ $on$ $tree$。
这个算法主要处理的是对于树上某一特征的不带修改子树统计问题,通常可以做到$O(nlogn)$。
大概做法就是先链剖,然后我考虑用一个全局数组$cnt$来表示某个值的出现次数,如果是暴力的做法的话,就是每次处理一个节点时暴力把整棵子树的贡献加入,同时更新答案,做完之后暴力把整棵子树的贡献消除,递归做儿子节点。
而$dsu$ $on$ $tree$的做法就是,每次先递归处理完轻儿子,然后再做重儿子。
到了统计答案的时候,同样是把贡献暴力加入,但是假如这个点是他的父亲节点的重儿子,那么不消除贡献(所以加入贡献的时候注意不要加到重儿子上去了),往上走,直到某个点是父亲的轻儿子再把整棵子树的贡献消除即可。
//It is made by ljh2000 //有志者,事竟成,破釜沉舟,百二秦关终属楚;苦心人,天不负,卧薪尝胆,三千越甲可吞吴。 #include <iostream> #include <cstdlib> #include <cstring> #include <cstdio> #include <cmath> #include <algorithm> #include <ctime> #include <vector> #include <queue> #include <map> #include <set> #include <string> #include <complex> #include <bitset> using namespace std; typedef long long LL; typedef long double LB; typedef complex<double> C; const double pi = acos(-1); const int MAXN = 100011; const int MAXM = 200011; int n,a[MAXN],cnt[MAXN],ecnt,first[MAXN],to[MAXM],next[MAXM],size[MAXN],son[MAXN],Son; LL sum,Max,ans[MAXN]; inline void link(int x,int y){ next[++ecnt]=first[x]; first[x]=ecnt; to[ecnt]=y; } inline int getint(){ int w=0,q=0; char c=getchar(); while((c<'0'||c>'9') && c!='-') c=getchar(); if(c=='-') q=1,c=getchar(); while (c>='0'&&c<='9') w=w*10+c-'0',c=getchar(); return q?-w:w; } inline void dfs(int x,int fa){ size[x]=1; for(int i=first[x];i;i=next[i]) { int v=to[i]; if(v==fa) continue; dfs(v,x); size[x]+=size[v]; if(size[v]>size[son[x]]) son[x]=v; } } inline void add(int x,int fa,int val){ cnt[ a[x] ]+=val; if(cnt[ a[x] ]>Max) Max=cnt[ a[x] ],sum=a[x]; else if(cnt[ a[x] ]==Max) sum+=a[x]; for(int i=first[x];i;i=next[i]) { int v=to[i]; if(v==fa || v==Son) continue; add(v,x,val); } } inline void solve(int x,int fa,bool T){ for(int i=first[x];i;i=next[i]) { int v=to[i]; if(v==fa || v==son[x]) continue; solve(v,x,1); } if(son[x]) solve(son[x],x,0),Son=son[x]; add(x,fa,1); Son=0; ans[x]=sum; if(T==1) add(x,fa,-1),sum=Max=0; } inline void work(){ n=getint(); for(int i=1;i<=n;i++) a[i]=getint(); int x,y; for(int i=1;i<n;i++) { x=getint(); y=getint(); link(x,y); link(y,x); } dfs(1,0); solve(1,0,1); for(int i=1;i<=n;i++) printf("%I64d ",ans[i]); } int main() { work(); return 0; } //有志者,事竟成,破釜沉舟,百二秦关终属楚;苦心人,天不负,卧薪尝胆,三千越甲可吞吴。