傲嬌少女幽香正在玩一個很是有趣的戰略類遊戲,原本這個遊戲的地圖其實還不算太大,幽香還能管得過來,可是不知道爲何如今的網遊廠商把遊戲的地圖越作越大,以致於幽香一眼根本看不過來,更別說和別人打仗了。ios
在打仗以前,幽香如今面臨一個很是基本的管理問題須要解決。 整個地圖是一個樹結構,一共有n塊空地,這些空地被n-1條帶權邊鏈接起來,使得每兩個點之間有一條惟一的路徑將它們鏈接起來。git
在遊戲中,幽香可能在空地上增長或者減小一些軍隊。同時,幽香能夠在一個空地上放置一個補給站。 若是補給站在點u上,而且空地v上有dv個單位的軍隊,那麼幽香天天就要花費dv*dist(u,v)的金錢來補給這些軍隊。數組
因爲幽香須要補給全部的軍隊,所以幽香總共就要花費爲Sigma(Dv*dist(u,v),其中1<=V<=N)的代價。其中dist(u,v)表示u個v在樹上的距離(惟一路徑的權和)。spa
由於遊戲的規定,幽香只能選擇一個空地做爲補給站。在遊戲的過程當中,幽香可能會在某些空地上製造一些軍隊,也可能會減小某些空地上的軍隊,進行了這樣的操做之後,出於經濟上的考慮,幽香每每能夠移動他的補給站從而省一些錢。code
可是因爲這個遊戲的地圖是在太大了,幽香沒法輕易的進行最優的安排,你能幫幫她嗎? 你能夠假定一開始全部空地上都沒有軍隊。blog
輸入格式:遞歸
第一行兩個數n和Q分別表示樹的點數和幽香操做的個數,其中點從1到n標號。 接下來n-1行,每行三個正整數a,b,c,表示a和b之間有一條邊權爲c的邊。 接下來Q行,每行兩個數u,e,表示幽香在點u上放了e單位個軍隊(若是e<0,就至關因而幽香在u上減小了|e|單位個軍隊,說白了就是du←du+e)。數據保證任什麼時候刻每一個點上的軍隊數量都是非負的。遊戲
輸出格式:get
對於幽香的每一個操做,輸出操做完成之後,天天的最小花費,也即若是幽香選擇最優的補給點進行補給時的花費。string
10 5 1 2 1 2 3 1 2 4 1 1 5 1 2 6 1 2 7 1 5 8 1 7 9 1 1 10 1 3 1 2 1 8 1 3 1 4 1
0 1 4 5 6
對於全部數據,1<=c<=1000, 0<=|e|<=1000, n<=10^5, Q<=10^5 很是神奇的是,對於全部數據,這棵樹上的點的度數都不超過20,且N,Q>=1
題解
噩夢……真的噩夢……我快被幽香給玩死了……
用這道題來理解動態澱粉質點分治的(由於當初捉迷藏那題直接學島孃的括號序列……就沒去再打一遍……),然而題解大佬們都直接默認咱們已經會動態點分而後直接上……結果我昨天看了一個晚上才弄懂……而後今天又花了一個早上碼完,交上去居然1A了難以想象
先說一下什麼是動態點分治吧。對於通常的點分治,由於咱們每一次都找出重心,因此每一次遞歸往左右子樹找的時候深度不會超過$O(log n)$層。可是若是有了修改操做怎麼辦呢?每修改一次點分一次麼?那怕是得T飛
咱們來看一下這道題目,它的每一次修改,更改的只有點權,樹的結構是沒有變化的(若是有的話怕是隻能上LCT了……),也就是說,每一次點分的時候找到的重心是不會改變的。那麼,咱們可不能夠把點分治每一層的重心給連成一棵樹呢?由於點分的遞歸層數只有$O(log n)$層,因此這棵點分樹的深度也是$O(log n)$的
那麼考慮一下在每個點分樹的節點維護什麼東西,對於每個節點,咱們維護他的子樹中的全部信息,也就是在原樹中它被選爲重心時的那個子樹的全部信息。修改的時候,只要從一個點開始在點分樹裏往上跳,並不斷更新信息便可。查詢一個點的時候,點分樹裏跳,不斷考慮與父親之間的貢獻就行了。
上面那一段看不懂也不要緊,由於我只是在口胡,僞裝本身已經很懂動態點分的樣子
那麼咱們具體來分析一下這道題目咱們要維護什麼
題目要求使$\sum d_v*dis(u,v)$最小,其中$d_v$爲$v$點的點權,$dis(u,v)$爲原樹中$u,v$兩點的距離。題目要求就是求帶權重心。假設咱們當前已經選定了點$v$爲答案,那麼考慮它的一個子節點$w$,若是把補給站從點$v$轉移到$w$,那麼$w$的全部子樹內的點到補給站的距離少了$dis(v,w)$,而其餘全部點到補給站的距離多了$dis(v,w)$。咱們假設$sum_v[v]$爲$v$的子樹內的點權和,那麼答案總共的變化量是$$dis(u,v)*(sum_v[v]-sum_v[w]-sum_v[w])$$
不難發現,當$sum_v[w]*2>sum_v[v]$的時候,答案的變化量是小於零的,也就是說代價減少,能夠變得更優。並且,知足這樣條件的點$w$最多隻有一個
那麼咱們能夠每一次選定一個點,而後看看往他的哪一個子樹走更優,若是沒有說明他本身就已是最優的了。這樣不斷下去確定能找到答案。
可是因爲原圖多是一條鏈,要怎麼作才能保證複雜度呢?咱們選擇在點分樹上走,每一次都跳到它下一層的重心,這樣能夠保證層數最多隻有$O(log n)$
而後考慮如何維護答案。不難發現,對於點分樹上一個點$v$,它的子樹中的點就是在點分治時它被選爲重心時的那棵樹上的點。考慮點分樹上的一對父子$u,v$,咱們設$sum_a[v]$表示$v$的子樹內的全部點到他的代價之和,$sum_b[v]$爲$v$的子樹內的全部點到$v$點父親(也就是點$u$)的距離之和,$sum_v[v]$仍是表示子樹的點權之和。那麼咱們設答案已經選定爲點$v$,加上$u$的不包括$v$的子樹後答案就是$sum_a[v]-sum_b[v]+sum_a[u]+sum_v[u]*dis(u,v)$。因而只要在點分樹上不斷找父親併合並,就能夠知道答案了
而後咱們只要能在修改時維護好這三個數組就能夠了!!!
至於修改時如何維護呢?咱們修改一個點以後,而後不斷在點分樹上往父節點跳,並不斷更新便可。查詢的時候也是,不斷跳併合並答案
而後又新學會了一招,用$RMQ O(1)$查詢$LCA$(只會倍增和樹剖的我瑟瑟發抖),總時間複雜度$O(nlog^2n)$
1 //minamoto 2 #include<cstdio> 3 #include<iostream> 4 #include<cstring> 5 #define ll long long 6 #define N 100005 7 #define inf 0x3f3f3f3f 8 #define rint register int 9 using namespace std; 10 #define getc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++) 11 char buf[1<<21],*p1=buf,*p2=buf; 12 template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;} 13 inline int read(){ 14 #define num ch-'0' 15 char ch;bool flag=0;int res; 16 while(!isdigit(ch=getc())) 17 (ch=='-')&&(flag=true); 18 for(res=num;isdigit(ch=getc());res=res*10+num); 19 (flag)&&(res=-res); 20 #undef num 21 return res; 22 } 23 char sr[1<<21],z[20];int C=-1,Z; 24 inline void Ot(){fwrite(sr,1,C+1,stdout),C=-1;} 25 inline void print(ll x){ 26 if(C>1<<20)Ot();if(x<0)sr[++C]=45,x=-x; 27 while(z[++Z]=x%10+48,x/=10); 28 while(sr[++C]=z[Z],--Z);sr[++C]='\n'; 29 } 30 struct G{ 31 int head[N],Next[N<<1],edge[N<<1],ver[N<<1],tot; 32 G(){tot=0;memset(head,0,sizeof(head));} 33 inline void add(int u,int v,int e){ 34 ver[++tot]=v,Next[tot]=head[u],head[u]=tot,edge[tot]=e; 35 } 36 }T1,T2; 37 int n,q,st[N<<1][18],logn[N<<1],bin[25],tp; 38 ll sum,ans,d[N],dis1[N],dis2[N],sumv[N]; 39 int dfn[N],num; 40 void dfs1(int u,int fa){ 41 st[dfn[u]=++num][0]=d[u]; 42 for(int i=T1.head[u];i;i=T1.Next[i]){ 43 int v=T1.ver[i]; 44 if(v==fa) continue; 45 d[v]=d[u]+T1.edge[i],dfs1(v,u),st[++num][0]=d[u]; 46 } 47 } 48 inline ll LCA(int a,int b){ 49 if(dfn[a]>dfn[b]) a^=b^=a^=b; 50 int k=logn[dfn[b]-dfn[a]+1]; 51 return min(st[dfn[a]][k],st[dfn[b]-bin[k]+1][k])<<1; 52 } 53 inline ll dis(int a,int b){return d[a]+d[b]-LCA(a,b);} 54 int sz[N],son[N],size,rt,fa[N];bool vis[N]; 55 void dfs2(int u,int fa){ 56 sz[u]=1,son[u]=0; 57 for(int i=T1.head[u];i;i=T1.Next[i]){ 58 int v=T1.ver[i]; 59 if(vis[v]||v==fa) continue; 60 dfs2(v,u),sz[u]+=sz[v],cmax(son[u],sz[v]); 61 } 62 cmax(son[u],size-sz[u]); 63 if(son[u]<son[rt]) rt=u; 64 } 65 void dfs3(int u){ 66 vis[u]=true; 67 for(int i=T1.head[u];i;i=T1.Next[i]){ 68 int v=T1.ver[i]; 69 if(vis[v]) continue; 70 rt=0,size=sz[v],son[0]=n+1; 71 dfs2(v,0),T2.add(u,rt,v),fa[rt]=u,dfs3(rt); 72 } 73 } 74 inline void update(int u,int val){ 75 sumv[u]+=val; 76 for(int p=u;fa[p];p=fa[p]){ 77 ll dist=dis(fa[p],u)*val; 78 dis1[fa[p]]+=dist; 79 dis2[p]+=dist; 80 sumv[fa[p]]+=val; 81 } 82 } 83 inline ll calc(int u){ 84 ll ans=dis1[u]; 85 for(int p=u;fa[p];p=fa[p]){ 86 ll dist=dis(fa[p],u); 87 ans+=dis1[fa[p]]-dis2[p]; 88 ans+=dist*(sumv[fa[p]]-sumv[p]); 89 } 90 return ans; 91 } 92 ll query(int u){ 93 ll ans=calc(u); 94 for(int i=T2.head[u];i;i=T2.Next[i]){ 95 ll tmp=calc(T2.edge[i]); 96 if(tmp<ans) return query(T2.ver[i]); 97 } 98 return ans; 99 } 100 void init(){ 101 n=read(),q=read(); 102 bin[0]=1,logn[0]=-1; 103 for(rint i=1;i<=20;++i) bin[i]=bin[i-1]<<1; 104 while(bin[tp+1]<=(n<<1)) ++tp; 105 for(rint i=1;i<=(n<<1);++i) logn[i]=logn[i>>1]+1; 106 for(rint i=1;i<n;++i){ 107 rint u=read(),v=read(),e=read(); 108 T1.add(u,v,e),T1.add(v,u,e); 109 } 110 dfs1(1,0),rt=0,son[0]=n+1,size=n,dfs2(1,0); 111 for(rint j=1;j<=tp;++j) 112 for(rint i=1;i+bin[j]-1<=(n<<1);++i) 113 st[i][j]=min(st[i][j-1],st[i+bin[j-1]][j-1]); 114 } 115 int main(){ 116 init(); 117 int LastOrder=rt;dfs3(rt); 118 while(q--){ 119 int x=read(),y=read();update(x,y); 120 print(query(LastOrder)); 121 } 122 Ot(); 123 return 0; 124 }