BZOJ 3924: [Zjoi2015]幻想鄉戰略遊戲(動態點分治)

題目描述

傲嬌少女幽香正在玩一個很是有趣的戰略類遊戲,原本這個遊戲的地圖其實還不算太大,幽香還能管得過來,可是不知道爲何如今的網遊廠商把遊戲的地圖越作越大,以致於幽香一眼根本看不過來,更別說和別人打仗了。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

輸入輸出樣例

輸入樣例#1:  複製
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
輸出樣例#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 }
相關文章
相關標籤/搜索