[ZJOI2015]幻想鄉戰略遊戲——動態點分治
帶修改下,邊點都帶權的重心
隨著變動的過程中,一些子樹內的點經過會經過一些公共邊。考慮能不能對這樣的子樹一起統計。
把樹上貢獻分塊。
考慮點分治演算法
不妨先把題目簡化一下:
假設沒有修改,多次詢問,每次給定一個s,求$\sum d_v*dis(s,v)$
為了讓一塊可以一起統計,
我們設:
$sum[x]:$x為根的點分治樹管轄部分的權值和
$df[x]$:x為根的點分治樹管轄部分到x分治樹father的帶權距離和$\sum d[v]*dis[v,fa[x]]$
$dm[x]:$ x為根的點分治樹管轄部分到x自己的帶權距離和
自底向上統計總距離
開始加上$dm[s]$,然後不斷加上$dis(s,fa)*(sum[fa]-sum[las])+dm[fa]-df[las]$
las代表上一個father
$dis(s,fa)$可以開始點分治的時候儲存(反正就log個)
對於一個s,可以$logn$找到答案
$dm,sum,df$也可以輕而易舉$logn$修改
問題是現在我們不知道s是哪一個。
我們比較熟悉一個叫貨倉選址問題。
這個重心,很類似於帶權中點。
從分治樹的root開始,列舉原樹上的出邊,到y,y所在的這一層的分治樹的father叫做son。如果存在一個子樹的sum[son]要大於x的剩下分支的點值總和,那麼重心一定在son裡。
(簡單證明:
如果存在sum[son]>sum[x]-sum[son],那麼這樣的son一定只有一個
存在往son走,減去的代價多,增加的代價少。存在代價更小的情況。(至少是y)
而往其他子樹走,代價一定是單調遞增上漲的。不可能更優。
所以,重心一定在son管轄的分治樹這塊區域
)
可以遞迴處理下去。
但是一個問題是,
如果到了son位置,但是對於son的1號子樹,權值和必須考慮上B的所有權值和。
否則肯定不能直接走。
我們還要返回去考慮father們的權值?
發現難以處理。因為之後的劃分可能較多。還要$logn$暴力找father
發現B的權值只有在詢問包含y的子樹的塊才會用到。
所以,我們乾脆直接把y點的權值加上sum[B]。回溯回來再減去。
然後就可以放心大膽查詢子樹的權值和了。
(因為這個小trick瞎寫了半天。。。。)
由於只會找到一個son,所以,每次找s的複雜度是:$O(20logn+log^2n)$
就可以了。
(雖然暴力$20*log^2n$每次也可以過,因為時限6s)
程式碼:
// luogu-judger-enable-o2#include<bits/stdc++.h> #define reg register int #define il inline #define numb (ch^'0') using namespace std; typedef long long ll; il void rd(int &x){ char ch;x=0;bool fl=false; while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true); for(x=numb;isdigit(ch=getchar());x=x*10+numb); (fl==true)&&(x=-x); } namespace Miracle{ const int N=1e5+5; int n,m; struct node{ int nxt,to; int son; ll val; }e[2*N]; int hd[N],cnt; void add(int x,int y,ll z){ e[++cnt].nxt=hd[x]; e[cnt].to=y; e[cnt].val=z; hd[x]=cnt; } ll has[N]; int rt; ll dm[N],df[N],sum[N]; ll dis[N][20]; int fa[N]; int sz[N],mxsz[N]; int nowsz; int gen; int dep[N]; bool vis[N]; void dfs1(int x,int ff,int d){ dep[x]=d; sz[x]=1;mxsz[x]=0; for(reg i=hd[x];i;i=e[i].nxt){ int y=e[i].to; if(y==ff) continue; if(vis[y]) continue; dfs1(y,x,d); sz[x]+=sz[y]; mxsz[x]=max(mxsz[x],sz[y]); } mxsz[x]=max(mxsz[x],nowsz-sz[x]); if(mxsz[x]<=nowsz/2){ rt=x; } } void dfs2(int x,int ff,int d){ sz[x]=1; for(reg i=hd[x];i;i=e[i].nxt){ int y=e[i].to; if(y==ff||vis[y]) continue; dis[y][d]=dis[x][d]+e[i].val; dfs2(y,x,d); sz[x]+=sz[y]; } } int divi(int x,int ff,int d){ rt=0; dfs1(x,0,d); dis[rt][d]=0; fa[rt]=ff; dfs2(rt,0,d); vis[rt]=1; int now=rt; for(reg i=hd[now];i;i=e[i].nxt){ int y=e[i].to; if(vis[y]) continue; nowsz=sz[y]; e[i].son=divi(y,now,d+1); } return now; } void upda(int x,ll c){//c is change int gg=x; int nd=dep[x]; while(x){ sum[x]+=c; df[x]+=c*dis[gg][nd-1]; dm[x]+=c*dis[gg][nd]; x=fa[x]; --nd; } } int query(int x,int d){ //cout<<" querying "<<x<<" || "<<" "<<d<<endl; for(reg i=hd[x];i;i=e[i].nxt){ int y=e[i].to; if(dep[y]<dep[x]) continue; int lp=e[i].son; if(sum[lp]>sum[x]-sum[lp]){ upda(y,sum[x]-sum[lp]); int ret=query(lp,d+1); upda(y,sum[lp]-sum[x]); return ret; } } return x; } ll calc(int x){ ll ret=dm[x]; int gg=x; int las=x; x=fa[x]; int nd=dep[x]; while(x){ ret+=(dm[x]-df[las])+(sum[x]-sum[las])*dis[gg][nd]; --nd; las=x; x=fa[x]; } return ret; } int main(){ rd(n);rd(m); int x,y; ll z; for(reg i=1;i<=n-1;++i){ rd(x);rd(y);scanf("%lld",&z); add(x,y,z);add(y,x,z); } nowsz=n; gen=divi(1,0,1); //cout<<" ffafaf "<<endl; // for(reg i=1;i<=n;++i){ // cout<<i<<" : "<<fa[i]<<endl; // } //cout<<" gen "<<gen<<endl; while(m--){ rd(x);scanf("%lld",&z); upda(x,z); has[x]+=z;//warning!!!! // for(reg i=1;i<=n;++i){ // cout<<i<<" : "<<sum[i]<<" "<<dm[i]<<" "<<df[i]<<endl; // } int core=query(gen,1); printf("%lld\n",calc(core)); } return 0; } } int main(){ // freopen("data.in","r",stdin); // freopen("my.out","w",stdout); Miracle::main(); return 0; } /* Author: *Miracle* Date: 2018/11/29 8:55:17 */