1. 程式人生 > >[BZOJ3924][ZJOI2015]捉迷藏(動態點分治)

[BZOJ3924][ZJOI2015]捉迷藏(動態點分治)

因此 最小花費 log 個數 廠商 超過 using inf amp

題目描述

傲嬌少女幽香正在玩一個非常有趣的戰略類遊戲,本來這個遊戲的地圖其實還不算太大,幽香還能管得過來,但是不知道為什麽現在的網遊廠商把遊戲的地圖越做越大,以至於幽香一眼根本看不過來,更別說和別人打仗了。

在打仗之前,幽香現在面臨一個非常基本的管理問題需要解決。 整個地圖是一個樹結構,一共有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]捉迷藏(動態點分治)