1. 程式人生 > >[ZJOI2015]幻想鄉戰略遊戲

[ZJOI2015]幻想鄉戰略遊戲

[ZJOI2015]幻想鄉戰略遊戲

動態點分治

  題目連結:https://www.luogu.org/problemnew/show/P3345

  參考:https://www.luogu.org/blog/zcysky/solution-p3345

  首先對於點分治下去的重心,我們連邊後可以得到一個新的樹(點分樹),樹上的每個點可以維護的是這課子樹的資訊

  點分樹的深度不會超過log n

  對於修改,我們可以從這課新樹對應的節點往上更新內容

 

  這題有一個貪心的方法,朝著最優點走會越走越優,反之越差

  點分樹的節點維護

    這顆子樹的軍隊總數              sum

    這顆子樹的軍隊到這個點的代價和        dis1

    這顆子樹的軍隊到這個點的點分樹的父節點的代價和   dis2

    

  對於修改,我們從這個原樹節點 對應的 點分樹節點 往上更新即可(n log n)

  對於查詢,我們會發現如果最優點在2個重心之間(其中一個重心是另外一個重心的點分樹的兒子)那麼兒子重心可能會比父重心差,就不會往兒子重心走了

  於是點分樹建邊時,可以維護一個資訊:父重心的原樹兒子w(在2個重心之間)

 

  於是查詢我們可以判斷,如果點分樹上,這個點連向兒子的邊的w比這個點優,就往這個點的兒子重心走。時間logn

  怎麼求出一個點的代價和呢?

    找到這個點對應點分樹上的點u

    對於這顆子樹的代價,直接+dis1u即可

    對於子樹外的代價,我們從u往上走

    每次走到一個點v,fa為v 的父親 +(\\$dis_fa\\$-\$dis2_v\$)+(\$sum_fa\$-\$sum_v\$)*dist(dist為fa到u的距離)

  時間logn

  於是查詢總時間為 n log n log n

  於是這題就做完了

  求兩點距離時,可以用到st表找lca,查詢為O1

  總時間n log^2 n

 

  1 #include<iostream>
  2 #include<cstdio>
  3
#define ll long long 4 using namespace std; 5 const ll M=2e5; 6 ll n,m; 7 ll read(){ 8 ll rex=0,f=1;char ch=getchar(); 9 while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} 10 while(ch>='0'&&ch<='9'){rex=rex*10+ch-'0';ch=getchar();} 11 return rex*f; 12 } 13 struct P{ll to,ne,w;}; 14 struct Lca{ 15 ll num,tot,pw[23],L[M],head[M],dep[M],fir[M],f[M<<1][23]; 16 P e[M<<1]; 17 void dfs(ll u,ll fa,ll w){ 18 f[fir[u]=++tot][0]=u;dep[u]=dep[fa]+w; 19 for(ll i=head[u];i;i=e[i].ne){ 20 ll v=e[i].to; 21 if(v!=fa){dfs(v,u,e[i].w);f[++tot][0]=u;} 22 } 23 } 24 void RMQ(){ 25 pw[0]=1;L[1]=0; 26 for(ll i=1;i<=20;++i)pw[i]=pw[i-1]<<1; 27 for(ll i=2;i<=tot;++i)L[i]=L[i>>1]+1; 28 for(ll j=1;j<=20;++j){ 29 for(ll i=1;i+pw[j]-1<=tot;++i){ 30 f[i][j]=dep[f[i][j-1]]<dep[f[i+pw[j-1]][j-1]]?f[i][j-1]:f[i+pw[j-1]][j-1]; 31 } 32 } 33 } 34 ll ask(ll l,ll r){ 35 l=fir[l],r=fir[r]; 36 if(l>r)swap(l,r); 37 ll k=L[r-l+1]; 38 return dep[f[l][k]]<dep[f[r-pw[k]+1][k]]?f[l][k]:f[r-pw[k]+1][k]; 39 } 40 ll dis(ll u,ll v){ 41 return dep[u]+dep[v]-2*dep[ask(u,v)]; 42 } 43 void solve(){ 44 n=read(),m=read(); 45 for(ll i=1,u,v,w;i<n;++i){ 46 u=read(),v=read(),w=read(); 47 e[++num]=(P){v,head[u],w};head[u]=num; 48 e[++num]=(P){u,head[v],w};head[v]=num; 49 } 50 dfs(1,0,0);RMQ(); 51 } 52 }s; 53 struct Divide{ 54 ll num,size,minn,rt,head[M],siz[M],f[M],sum[M],dis1[M],dis2[M]; 55 bool vis[M]; 56 P e[M<<1]; 57 void add(ll u,ll v,ll w){ 58 e[++num]=(P){v,head[u],w};head[u]=num; 59 } 60 void getrt(ll u,ll fa){ 61 ll ma=0;siz[u]=1; 62 for(ll i=s.head[u];i;i=s.e[i].ne){ 63 ll v=s.e[i].to; 64 if(v!=fa&&!vis[v]){ 65 getrt(v,u); 66 siz[u]+=siz[v]; 67 ma=max(ma,siz[v]); 68 } 69 } 70 ma=max(ma,size-siz[u]); 71 if(ma<minn){minn=ma,rt=u;} 72 } 73 void work(ll u){ 74 vis[u]=1; 75 for(ll i=s.head[u];i;i=s.e[i].ne){ 76 ll v=s.e[i].to;if(vis[v])continue; 77 minn=1e12; 78 size=siz[v];getrt(v,0); 79 add(u,rt,v);f[rt]=u; 80 work(rt); 81 } 82 } 83 void update(ll u,ll v){ 84 sum[u]+=v; 85 for(ll i=u;f[i];i=f[i]){ 86 ll dist=s.dis(u,f[i]); 87 dis1[f[i]]+=dist*v; 88 dis2[i]+=dist*v; 89 sum[f[i]]+=v; 90 } 91 } 92 ll calc(ll u){ 93 ll ans=dis1[u]; 94 for(ll i=u;f[i];i=f[i]){ 95 ll dist=s.dis(f[i],u); 96 ans+=dis1[f[i]]-dis2[i]; 97 ans+=dist*(sum[f[i]]-sum[i]); 98 } 99 return ans; 100 } 101 ll query(ll u){ 102 ll ans=calc(u); 103 for(ll i=head[u];i;i=e[i].ne){ 104 ll tmp=calc(e[i].w); 105 ll v=e[i].to; 106 if(tmp<ans)return query(v); 107 } 108 return ans; 109 } 110 void solve(){ 111 minn=1e12;size=n;getrt(1,0); 112 ll la=rt;work(rt); 113 for(ll i=1,u,v;i<=m;++i){ 114 u=read(),v=read(); 115 update(u,v); 116 printf("%lld\n",query(la)); 117 } 118 } 119 }t; 120 int main(){ 121 s.solve(); 122 t.solve(); 123 return 0; 124 }