[BZOJ3924][ZJOI2015]捉迷藏(動態點分治)
題目描述
傲嬌少女幽香正在玩一個非常有趣的戰略類遊戲,本來這個遊戲的地圖其實還不算太大,幽香還能管得過來,但是不知道為什麽現在的網遊廠商把遊戲的地圖越做越大,以至於幽香一眼根本看不過來,更別說和別人打仗了。
在打仗之前,幽香現在面臨一個非常基本的管理問題需要解決。 整個地圖是一個樹結構,一共有n塊空地,這些空地被n-1條帶權邊連接起來,使得每兩個點之間有一條唯一的路徑將它們連接起來。
在遊戲中,幽香可能在空地上增加或者減少一些軍隊。同時,幽香可以在一個空地上放置一個補給站。 如果補給站在點u上,並且空地v上有dv個單位的軍隊,那麽幽香每天就要花費dv*dist(u,v)的金錢來補給這些軍隊。
由於幽香需要補給所有的軍隊,因此幽香總共就要花費為Sigma(Dv*dist(u,v),其中1<=V<=N)的代價。其中dist(u,v)表示u個v在樹上的距離(唯一路徑的權和)。
因為遊戲的規定,幽香只能選擇一個空地作為補給站。在遊戲的過程中,幽香可能會在某些空地上制造一些軍隊,也可能會減少某些空地上的軍隊,進行了這樣的操作以後,出於經濟上的考慮,幽香往往可以移動他的補給站從而省一些錢。
但是由於這個遊戲的地圖是在太大了,幽香無法輕易的進行最優的安排,你能幫幫她嗎? 你可以假定一開始所有空地上都沒有軍隊。
輸入輸出格式
輸入格式:
第一行兩個數n和Q分別表示樹的點數和幽香操作的個數,其中點從1到n標號。 接下來n-1行,每行三個正整數a,b,c,表示a和b之間有一條邊權為c的邊。 接下來Q行,每行兩個數u,e,表示幽香在點u上放了e單位個軍隊(如果e<0,就相當於是幽香在u上減少了|e|單位個軍隊,說白了就是 du←du+e)。數據保證任何時刻每個點上的軍隊數量都是非負的。
輸出格式:
對於幽香的每個操作,輸出操作完成以後,每天的最小花費,也即如果幽香選擇最優的補給點進行補給時的花費。
輸入輸出樣例
輸入樣例#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
建出點分樹,直接用動態點分治即可,不需要配合高級數據結構。
註意lg[]對數表預處理的範圍是[1,2n]的。
1 #include<cstdio> 2 #include<algorithm> 3 #define rep(i,l,r) for (int i=l; i<=r; i++) 4 #define For(i,x) for (int i=h[x],k; i; i=nxt[i]) 5 typedef long long ll; 6 using namespace std; 7 8 const int N=200100,inf=1000000000; 9 int n,Q,x,y,u,v,w,rt,lg[N],a[N],pos[N],vis[N],st[N][20],tot,fa[N],f[N],sz[N],d[N]; 10 ll dis1[N],dis2[N],sm[N]; 11 12 struct E{ 13 int to[N<<1],nxt[N<<1],h[N],val[N<<1],cnt; 14 void add(int u,int v,int w){ to[++cnt]=v; val[cnt]=w; nxt[cnt]=h[u]; h[u]=cnt; } 15 void find(int x,int fa,int S,int &rt){ 16 sz[x]=1; f[x]=0; 17 For(i,x) if ((k=to[i])!=fa && !vis[k]) 18 find(k,x,S,rt),sz[x]+=sz[k],f[x]=max(f[x],sz[k]); 19 f[x]=max(f[x],S-sz[x]); 20 if (f[x]<=f[rt]) rt=x; 21 } 22 void dfs(int x,int fa){ 23 pos[x]=++tot; a[tot]=d[x]; 24 For(i,x) if (fa!=(k=to[i])) d[k]=d[x]+val[i],dfs(k,x),a[++tot]=d[x]; 25 } 26 }G,G1; 27 28 void build(int x){ 29 vis[x]=1; 30 for (int i=G.h[x],k; i; i=G.nxt[i]) if (!vis[k=G.to[i]]){ 31 int rt=0; f[rt]=inf; G.find(k,0,sz[k],rt); G1.add(x,rt,k); fa[rt]=x; build(rt); 32 } 33 } 34 35 void getst(){ 36 rep(i,1,tot) st[i][0]=a[i]; 37 for (int j=1; j<=18; j++) 38 rep(i,1,tot-(1<<j)+1) st[i][j]=min(st[i][j-1],st[i+(1<<(j-1))][j-1]); 39 } 40 41 int que(int l,int r){ int t=lg[r-l+1]; return min(st[l][t],st[r-(1<<t)+1][t]); } 42 int dis(int x,int y){ int a=pos[x],b=pos[y]; if (a>b) swap(a,b); return d[x]+d[y]-2*que(a,b); } 43 44 void ins(int x,int k){ 45 sm[x]+=k; 46 for (int i=x; fa[i]; i=fa[i]){ 47 int D=dis(fa[i],x); 48 dis1[fa[i]]+=1ll*D*k; dis2[i]+=1ll*D*k; sm[fa[i]]+=k; 49 } 50 } 51 52 ll calc(int u){ 53 ll ans=dis1[u]; 54 for (int i=u; fa[i]; i=fa[i]){ 55 int D=dis(fa[i],u); ans+=dis1[fa[i]]-dis2[i]; ans+=D*(sm[fa[i]]-sm[i]); 56 } 57 return ans; 58 } 59 60 ll que(int u){ 61 ll ans=calc(u); 62 for (int i=G1.h[u]; i; i=G1.nxt[i]){ 63 ll tmp=calc(G1.val[i]); 64 if (tmp<ans) return que(G1.to[i]); 65 } 66 return ans; 67 } 68 69 int main(){ 70 scanf("%d%d",&n,&Q); 71 lg[1]=0; rep(i,2,200010) lg[i]=lg[i>>1]+1; 72 rep(i,1,n-1) scanf("%d%d%d",&u,&v,&w),G.add(u,v,w),G.add(v,u,w); 73 G.dfs(1,0); getst(); f[rt=0]=inf; G.find(1,0,n,rt); build(rt); 74 while (Q--) scanf("%d%d",&x,&y),ins(x,y),printf("%lld\n",que(rt)); 75 return 0; 76 }
[BZOJ3924][ZJOI2015]捉迷藏(動態點分治)