Codeforces.600E.Lomsat gelral(dsu on tree)
阿新 • • 發佈:2018-11-25
\(Description\)
給定一棵樹。求以每個點為根的子樹中,出現次數最多的顏色的和。
\(Solution\)
dsu on tree模板題.
用sum[i]表示出現次數為i的顏色的和,cnt[i]表示出現次數為i的顏色有多少個(其實有個Max表示當前最多的次數,和tm[i]就好了),然後就這樣了。。
再寫一遍dsu on tree大體過程:(設當前點為x)
計算輕兒子子樹的答案,並刪掉輕兒子的貢獻(大多數時候);
計算重兒子子樹的答案,保留重兒子的貢獻(有時候不需要?);
加入x輕兒子子樹的貢獻;
得到x的答案;
如果x不是重兒子,則刪掉它整個子樹的貢獻(本題直接將Max,Sum清0就好了)。
複雜度證明:每個點會在它上面的每條輕邊處暴力統計一次。而每個點到根節點的路徑上只有\(O(\log n)\)條輕邊。
可以用DFS序代替DFS減少一些常數。
優化:(DFS序)
//62ms 13400KB #include <cstdio> #include <cctype> #include <algorithm> //#define gc() getchar() #define MAXIN 300000 #define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++) typedef long long LL; const int N=1e5+5; int skip,Enum,H[N],nxt[N<<1],to[N<<1],Max,col[N],sz[N],son[N],tm[N],A[N],L[N],R[N]; LL Sum,Ans[N]; char IN[MAXIN],*SS=IN,*TT=IN; inline int read() { int now=0;register char c=gc(); for(;!isdigit(c);c=gc()); for(;isdigit(c);now=now*10+c-'0',c=gc()); return now; } inline void AE(int u,int v) { to[++Enum]=v, nxt[Enum]=H[u], H[u]=Enum; to[++Enum]=u, nxt[Enum]=H[v], H[v]=Enum; } void DFS1(int x,int fa) { static int Index=0; A[L[x]=++Index]=x; int mx=0; sz[x]=1; for(int i=H[x],v; i; i=nxt[i]) if((v=to[i])!=fa) { DFS1(v,x), sz[x]+=sz[v]; if(sz[v]>mx) mx=sz[v], son[x]=v; } R[x]=Index; } inline void Add(int c) { if(++tm[c]>Max) Max=tm[c], Sum=c; else if(tm[c]==Max) Sum+=c; } void Solve(int x,int fa,int keep) { for(int i=H[x]; i; i=nxt[i]) if(to[i]!=fa && to[i]!=son[x]) Solve(to[i],x,0); if(son[x]) Solve(son[x],x,1); Add(col[x]); for(int i=H[x],v; i; i=nxt[i]) if((v=to[i])!=fa && v!=son[x]) for(int j=L[v]; j<=R[v]; ++j) Add(col[A[j]]); Ans[x]=Sum; if(!keep) { Max=Sum=0; for(int i=L[x]; i<=R[x]; ++i) --tm[col[A[i]]]; } } int main() { int n=read(); for(int i=1; i<=n; ++i) col[i]=read(); for(int i=1; i<n; ++i) AE(read(),read()); DFS1(1,1), Solve(1,1,1); for(int i=1; i<=n; ++i) printf("%I64d ",Ans[i]); return 0; }
未優化:
//93ms 9100KB #include <cstdio> #include <cctype> #include <algorithm> //#define gc() getchar() #define MAXIN 300000 #define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++) typedef long long LL; const int N=1e5+5; int skip,Enum,H[N],nxt[N<<1],to[N<<1],Max,col[N],sz[N],son[N],tm[N]; LL Sum,Ans[N]; char IN[MAXIN],*SS=IN,*TT=IN; inline int read() { int now=0;register char c=gc(); for(;!isdigit(c);c=gc()); for(;isdigit(c);now=now*10+c-'0',c=gc()); return now; } inline void AE(int u,int v) { to[++Enum]=v, nxt[Enum]=H[u], H[u]=Enum; to[++Enum]=u, nxt[Enum]=H[v], H[v]=Enum; } void DFS1(int x,int fa) { int mx=0; sz[x]=1; for(int i=H[x],v; i; i=nxt[i]) if((v=to[i])!=fa) { DFS1(v,x), sz[x]+=sz[v]; if(sz[v]>mx) mx=sz[v], son[x]=v; } } void Add(int x,int fa) { if(++tm[col[x]]>Max) Max=tm[col[x]], Sum=col[x]; else if(tm[col[x]]==Max) Sum+=col[x]; for(int i=H[x]; i; i=nxt[i]) if(to[i]!=fa && to[i]!=skip) Add(to[i],x); } void Del(int x,int fa) { --tm[col[x]]; for(int i=H[x]; i; i=nxt[i]) if(to[i]!=fa) Del(to[i],x); } void Solve(int x,int fa,int keep) { for(int i=H[x]; i; i=nxt[i]) if(to[i]!=fa && to[i]!=son[x]) Solve(to[i],x,0); if(son[x]) Solve(son[x],x,1), skip=son[x]; Add(x,fa), Ans[x]=Sum, skip=0; if(!keep) Max=Sum=0, Del(x,fa); } int main() { int n=read(); for(int i=1; i<=n; ++i) col[i]=read(); for(int i=1; i<n; ++i) AE(read(),read()); DFS1(1,1), Solve(1,1,1); for(int i=1; i<=n; ++i) printf("%I64d ",Ans[i]); return 0; }