CF960E Alternating Tree 點分治
阿新 • • 發佈:2020-11-06
很久沒寫點分治了 十分拉跨 寫了一下午
首先我們觀察題目 如果一條路徑的點數為偶數 那麼這條路徑是無效的 因為一正一反求和恰好為0
而如果為奇數的話 就得*2
那麼問題轉換成了 求一棵樹 所有點數為奇數的路徑權值和(權值的計算方法就是他給的方法)
點分治搞一下就行
另外關於點分治的一些細節:如果算邊權的話比較好寫 如果是算點權的話 可以先把被分治的點拿出來 單獨計算(類似於算邊權)
#include<bits/stdc++.h> using namespace std; const int N = 2e5+100; typedef long long ll; const ll mod = 1e9+7; const int inf = 1e9; ll val[N],op[2],ct[2],ans; int n,_size,mx,rt; int cur,h[N],nex[N<<1],to[N<<1],siz[N],vis[N]; void add_edge(int x,int y){ to[++cur]=y;nex[cur]=h[x];h[x]=cur; } void getrt(int u,int fa){ siz[u]=1; int num=0; for(int i = h[u]; i; i = nex[i]){ int v = to[i]; if(v==fa||vis[v]) continue; getrt(v,u); siz[u]+=siz[v]; if(siz[v]>num) num=siz[v]; } if(_size-siz[u]>num) num=_size-siz[u]; if(num<mx) mx=num,rt=u; } void cal(int u,int fa,int dep,ll nowsum,ll add){ if(dep){ nowsum=(nowsum+val[u])%mod; ans=(ans+(ct[dep]*(nowsum-add+mod)%mod+op[dep])%mod)%mod; }else{ nowsum=(nowsum-val[u]+mod)%mod; ans=(ans+(ct[dep]*(-nowsum+add+mod)%mod-op[dep]+mod)%mod)%mod; } for(int i = h[u]; i; i = nex[i]){ int v = to[i]; if(v==fa||vis[v]) continue; cal(v,u,dep^1,nowsum,add); } } void ins(int u,int fa,int dep,ll s){ if(dep) s=(s+val[u])%mod; else s=(s-val[u]+mod)%mod; ct[dep]++; op[dep]=(op[dep]+s)%mod; for(int i = h[u]; i; i = nex[i]){ int v = to[i]; if(v==fa||vis[v]) continue; ins(v,u,dep^1,s); } } void divide(int u){ vis[u]=1; op[0]=0; op[1]=0; ct[0]=1,ct[1]=0; for(int i = h[u]; i; i = nex[i]){ int v = to[i]; if(vis[v]) continue; cal(v,u,1,0,val[u]); ins(v,u,1,0); } for(int i = h[u]; i; i = nex[i]){ int v = to[i]; if(vis[v]) continue; _size=siz[v]; mx=inf; getrt(v,u); divide(rt); } } int main(){ scanf("%d",&n); for(int i = 1; i <= n; i++) scanf("%lld",&val[i]),val[i]=(val[i]+mod)%mod; for(int i = 1; i < n; i++){ int x,y; scanf("%d%d",&x,&y); add_edge(x,y); add_edge(y,x); } _size=n; mx=inf; getrt(1,0); divide(rt); ans=(ans*2ll)%mod; for(int i = 1; i <= n; i++) ans=(ans+val[i])%mod; printf("%lld\n",ans); return 0; } /* 4 1 2 3 4 1 2 2 3 3 4 */