1. 程式人生 > >bzoj 3924: [Zjoi2015]幻想鄉戰略遊戲

bzoj 3924: [Zjoi2015]幻想鄉戰略遊戲

oid main 距離 esp space 當前 路徑 但是 std

Description

傲嬌少女幽香正在玩一個非常有趣的戰略類遊戲,本來這個遊戲的地圖其實還不算太大,幽香還能管得過來,但是不知道為什麽現在的網遊廠商把遊戲的地圖越做越大,以至於幽香一眼根本看不過來,更別說和別人打仗了。
在打仗之前,幽香現在面臨一個非常基本的管理問題需要解決。 整個地圖是一個樹結構,一共有n塊空地,這些空地被n-1條帶權邊連接起來,使得每兩個點之間有一條唯一的路徑將它們連接起來。
在遊戲中,幽香可能在空地上增加或者減少一些軍隊。同時,幽香可以在一個空地上放置一個補給站。 如果補給站在點u上,並且空地v上有dv個單位的軍隊,那麽幽香每天就要花費dvdist(u,v)的金錢來補給這些軍隊。

由於幽香需要補給所有的軍隊,因此幽香總共就要花費為Sigma(Dvdist(u,v),其中1<=V<=N)的代價。其中dist(u,v)表示u個v在樹上的距離(唯一路徑的權和)。
因為遊戲的規定,幽香只能選擇一個空地作為補給站。在遊戲的過程中,幽香可能會在某些空地上制造一些軍隊,也可能會減少某些空地上的軍隊,進行了這樣的操作以後,出於經濟上的考慮,幽香往往可以移動他的補給站從而省一些錢。
但是由於這個遊戲的地圖是在太大了,幽香無法輕易的進行最優的安排,你能幫幫她嗎? 你可以假定一開始所有空地上都沒有軍隊。

Solution

正解:動態點分
基於本題性質:度數不超過20,否則不能這麽做

建立重心樹,我們可以\(logn\)的算出建立在當前點的代價
\(dis[x]\) 表示x所在的塊到x的距離\(dis*dv\)之和,\(disf[x]\)表示x所在的塊到x的重心樹上的父親距離\(L*dv\)的距離之和,\(w[x]\) 為x塊內的\(dv\)之和
那麽遍歷父親就可以算出代價了,考慮兩個塊貢獻合並:u為計算的點,設x為兒子,v為父親,合並的貢獻為:\(dis[v]+(val[v]-val[x])*L(v,u)+disf[x]\)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const
int N=100005; int head[N],nxt[N<<2],to[N<<2],num=0,c[N<<2],n,m,Head[N]; inline void link(int x,int y,int z){ nxt[++num]=head[x];to[num]=y;c[num]=z;head[x]=num;} namespace tr{ int dep[N],sz[N],son[N],fa[N],top[N],dis[N]; inline void dfs1(int x){ sz[x]=1; for(int i=head[x];i;i=nxt[i]){ int u=to[i];if(dep[u])continue; dep[u]=dep[x]+1;dis[u]=dis[x]+c[i];fa[u]=x;dfs1(u); sz[x]+=sz[u];if(sz[u]>sz[son[x]])son[x]=u; } } inline void dfs2(int x,int tp){ top[x]=tp; if(son[x])dfs2(son[x],tp); for(int i=head[x];i;i=nxt[i]) if(to[i]!=fa[x] && to[i]!=son[x])dfs2(to[i],to[i]); } inline int lca(int x,int y){ while(top[x]!=top[y]){ if(dep[top[x]]<dep[top[y]])swap(x,y); x=fa[top[x]]; }return dep[x]<dep[y]?x:y; } inline int getdis(int x,int y){return dis[x]+dis[y]-(dis[lca(x,y)]<<1);} void main(){dep[1]=1;dfs1(1);dfs2(1,1);} } int rt=0,sz[N],son[N]={N},sum,S,fa[N];bool vis[N]; inline void link2(int x,int y,int v){ nxt[++num]=Head[x];to[num]=y;Head[x]=num;c[num]=v;} inline void getroot(int x,int last){ sz[x]=1;son[x]=0; for(int i=head[x];i;i=nxt[i]){ int u=to[i]; if(u==last || vis[u])continue; getroot(u,x);sz[x]+=sz[u]; son[x]=max(son[x],sz[u]); } son[x]=max(son[x],sum-sz[x]); if(son[x]<son[rt])rt=x; } inline void solve(int x){ vis[x]=1; for(int i=head[x];i;i=nxt[i]){ int u=to[i];if(vis[u])continue; rt=0;sum=sz[u];getroot(u,x);link2(x,rt,to[i]);fa[rt]=x; solve(rt); } } ll val[N],dis[N],disf[N]; inline void Modify(int x,int y){ for(int v=x;v;v=fa[v]){ val[v]+=y; dis[v]+=1ll*y*tr::getdis(x,v); if(fa[v])disf[v]+=1ll*y*tr::getdis(x,fa[v]); } } inline ll ask(int x){ ll ret=dis[x];int v=x; while(fa[v]){ ret+=dis[fa[v]]+(val[fa[v]]-val[v])*tr::getdis(fa[v],x)-disf[v]; v=fa[v]; } return ret; } inline ll qry(int x){ ll ret=ask(x); for(int i=Head[x];i;i=nxt[i]) if(ask(c[i])<ret)return qry(to[i]); return ret; } void work() { int x,y,z; scanf("%d%d",&n,&m); for(int i=1;i<n;i++){ scanf("%d%d%d",&x,&y,&z); link(x,y,z);link(y,x,z); } tr::main(); sum=n;rt=0;getroot(1,1);solve(S=rt); while(m--){ scanf("%d%d",&x,&y); Modify(x,y); printf("%lld\n",qry(S)); } } int main() { freopen("pp.in","r",stdin); freopen("pp.out","w",stdout); work(); return 0; }

bzoj 3924: [Zjoi2015]幻想鄉戰略遊戲