洛谷P2664 樹上游戲(點分治)

傳送門ios

題解git

  由於一個sb錯誤調了一個晚上……鬼曉得我爲何$solve(rt)$會寫成$solve(v)$啊!!!一個$O(logn)$被我硬生生寫成$O(n)$了居然還能過$5$個點……話說還一直覺得只有動態點分會很難沒想到通常點分都這麼可啪……%%%大佬spa

  咱們考慮一下,對於一棵樹,咱們要處理的是子樹對根的答案的貢獻,以及通過根的路徑的貢獻(也就是$LCA$爲根的點對的答案)。code

  對於樹中的一個點$i$,若是$i$的顏色是在$i$到根的路徑上第一次出現,那麼全部與$i$的$LCA$爲根的點,都能與$i$的子樹造成點對,且$i$的顏色會對那些點產生產生貢獻$size[i]$(先不考慮其餘子樹中是否有相同顏色)。blog

  那麼咱們考慮$dfs$一遍整棵樹,並記錄,全部顏色產生的貢獻$col[i]$以及貢獻總和$sum$。而後$dfs$子樹,並把其中的全部顏色的貢獻給減掉。而後考慮子樹中的一個點,設$x$爲到根上的全部點的貢獻總和,$num$表示根到節點上的顏色個數,$y=size[root]-size[該子樹]$,那麼$ans[j]+=sum-x+num*size[y]$get

  而後最後記得加上對根節點的答案的貢獻$ans[root]+=sum-col[根的顏色]+size[root]$string

  不得不說思路真的挺妙的it

  1 //minamoto
  2 #include<iostream>
  3 #include<cstdio>
  4 #include<cstring>
  5 #define ll long long
  6 using namespace std;
  7 #define getc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
  8 char buf[1<<21],*p1=buf,*p2=buf;
  9 template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
 10 inline int read(){
 11     #define num ch-'0'
 12     char ch;bool flag=0;int res;
 13     while(!isdigit(ch=getc()))
 14     (ch=='-')&&(flag=true);
 15     for(res=num;isdigit(ch=getc());res=res*10+num);
 16     (flag)&&(res=-res);
 17     #undef num
 18     return res;
 19 }
 20 char sr[1<<21],z[20];int C=-1,Z;
 21 inline void Ot(){fwrite(sr,1,C+1,stdout),C=-1;}
 22 inline void print(ll x){
 23     if(C>1<<20)Ot();if(x<0)sr[++C]=45,x=-x;
 24     while(z[++Z]=x%10+48,x/=10);
 25     while(sr[++C]=z[Z],--Z);sr[++C]='\n';
 26 }
 27 const int N=200005;
 28 int head[N],Next[N<<1],ver[N<<1],c[N],son[N],sz[N],size,cnt[N];
 29 ll col[N],ans[N],much,sum,num;
 30 int tot,n,rt;bool vis[N];
 31 inline void add(int u,int v){
 32     ver[++tot]=v,Next[tot]=head[u],head[u]=tot;
 33     ver[++tot]=u,Next[tot]=head[v],head[v]=tot;
 34 }
 35 void findrt(int u,int fa){
 36     sz[u]=1,son[u]=0;
 37     for(int i=head[u];i;i=Next[i]){
 38         int v=ver[i];
 39         if(!vis[v]&&v!=fa){
 40             findrt(v,u);
 41             sz[u]+=sz[v];
 42             cmax(son[u],sz[v]);
 43         }
 44     }
 45     cmax(son[u],size-sz[u]);
 46     if(son[u]<son[rt]) rt=u;
 47 }
 48 void dfs1(int u,int fa){
 49     //要從新dfs一次,不能直接在找根時的樹上作(否則子樹和之類的會出錯) 
 50     //順便維護各類東西 
 51     sz[u]=1,++cnt[c[u]];
 52     for(int i=head[u];i;i=Next[i]){
 53         int v=ver[i];
 54         if(!vis[v]&&v!=fa){
 55             dfs1(v,u),sz[u]+=sz[v];
 56         }
 57     }
 58     if(cnt[c[u]]==1) sum+=sz[u],col[c[u]]+=sz[u];
 59     --cnt[c[u]];
 60 }
 61 void change(int u,int fa,int val){
 62     ++cnt[c[u]];
 63     for(int i=head[u];i;i=Next[i]){
 64         int v=ver[i];
 65         if(!vis[v]&&v!=fa) change(v,u,val);
 66     }
 67     if(cnt[c[u]]==1) sum+=sz[u]*val,col[c[u]]+=sz[u]*val;
 68     --cnt[c[u]];
 69 }
 70 void dfs2(int u,int fa){
 71     //把這棵子樹裏的顏色的影響消除掉
 72     //順便更新答案 
 73     ++cnt[c[u]];
 74     if(cnt[c[u]]==1) sum-=col[c[u]],++num;
 75     ans[u]+=sum+num*much;
 76     for(int i=head[u];i;i=Next[i]){
 77         int v=ver[i];
 78         if(!vis[v]&&v!=fa) dfs2(v,u);
 79     }
 80     if(cnt[c[u]]==1) sum+=col[c[u]],--num;
 81     --cnt[c[u]];
 82 }
 83 void clear(int u,int fa){
 84     cnt[c[u]]=col[c[u]]=0;
 85     for(int i=head[u];i;i=Next[i]){
 86         int v=ver[i];
 87         if(!vis[v]&&v!=fa) clear(v,u);
 88     }
 89 }
 90 void did(int u){
 91     //直接帶進去亂搞 
 92     dfs1(u,0);
 93     ans[u]+=sum-col[c[u]]+sz[u];
 94     for(int i=head[u];i;i=Next[i]){
 95         int v=ver[i];
 96         if(!vis[v]){
 97             //dfs,而後把各類影響消除掉 
 98             ++cnt[c[u]];
 99             sum-=sz[v];
100             col[c[u]]-=sz[v];
101             change(v,u,-1);
102             --cnt[c[u]];
103             much=sz[u]-sz[v];
104             dfs2(v,u);
105             ++cnt[c[u]];
106             sum+=sz[v];
107             col[c[u]]+=sz[v];
108             change(v,u,1);
109             --cnt[c[u]];
110         }
111     }
112     sum=0,num=0,clear(u,0);
113 }
114 void solve(int u){
115     did(u),vis[u]=true;
116     for(int i=head[u];i;i=Next[i]){
117         int v=ver[i];
118         if(!vis[v]){
119             rt=0,size=sz[v];
120             findrt(v,0),solve(rt);
121         }
122     }
123 }
124 int main(){
125     n=read();
126     for(int i=1;i<=n;++i) c[i]=read();
127     for(int i=1;i<n;++i){
128         int u=read(),v=read();
129         add(u,v);
130     }
131     son[0]=n+1,rt=0,size=n;
132     findrt(1,0),solve(rt);
133     for(int i=1;i<=n;++i) print(ans[i]);
134     Ot();
135     return 0;
136 }
相關文章
相關標籤/搜索