luogu_P3345[zjoi2015]幻想鄉戰略遊戲
傳送門
Description
傲嬌少女幽香正在玩一個非常有趣的戰略類遊戲,本來這個遊戲的地圖其實還不算太大,幽香還能管得過來,但是不知道為什麼現在的網遊廠商把遊戲的地圖越做越大,以至於幽香一眼根本看不過來,更別說和別人打仗了。
在打仗之前,幽香現在面臨一個非常基本的管理問題需要解決。 整個地圖是一個樹結構,一共有\(n\)塊空地,這些空地被\(n-1\)條帶權邊連線起來,使得每兩個點之間有一條唯一的路徑將它們連線起來。
在遊戲中,幽香可能在空地上增加或者減少一些軍隊。同時,幽香可以在一個空地上放置一個補給站。 如果補給站在點\(u\)上,並且空地v上有\(dv\)
個單位的軍隊,那麼幽香每天就要花費\(dv*dist(u,v)\)的金錢來補給這些軍隊。由於幽香需要補給所有的軍隊,因此幽香總共就要花費為\(\sum Dv*dist(u,v)\) (\(1\leq V \leq N\))的代價。其中\(dist(u,v)\)表示u個v在樹上的距離(唯一路徑的權和)。
因為遊戲的規定,幽香只能選擇一個空地作為補給站。在遊戲的過程中,幽香可能會在某些空地上製造一些軍隊,也可能會減少某些空地上的軍隊,進行了這樣的操作以後,出於經濟上的考慮,幽香往往可以移動他的補給站從而省一些錢。
但是由於這個遊戲的地圖是在太大了,幽香無法輕易的進行最優的安排,你能幫幫她嗎? 你可以假定一開始所有空地上都沒有軍隊。
Solution
題意:樹上點權修改,詢問帶權重心
大大加深了對點分樹的理解?
首先,點分樹有個性質:點分樹上的兩點的lca在原樹的兩點路徑上,這樣,我們通過記錄點分樹上一點到它的祖先的在原樹上的距離,就可以得到任意兩點在原樹上的距離啦
帶權重心是什麼咧?
可以當做它是這樣一個點,已這個點為根的每個子樹的權重都不超過原樹的總size。
為什麼?假如找到了這個點,那麼無論把它移動進入哪個子樹,代價都會變得更大,讀者可以自行證明。
而我們用上面的做法,就可以得到樹的帶權重心。
對於這道題:
先預處理出點分樹,記錄下每個點在點分樹上的孩子和祖先以及和每個祖先的實際距離(便於以後查詢)
記錄下點分樹上每個節點,在原樹上所代表的聯通塊的根節點\(HR\)(這裡指的是深度最小的那個點)
為什麼要記錄這個東西咧?
因為我們在尋找帶權重心的時候,每進入一個點,就要更改某些點的權重,因為要算上除了這個聯通塊外的權重,而需要更改的點就是這個\(HR\),可以理解為,它是這個聯通塊的邊界
當然找到重心後,你得把你做的更改全都改回來
我們考慮每次更改會有什麼影響,首先,總權重增加\(e\),點分樹上的相關節點的總權重也會發生變化,還有就是要即使更新相關節點的代價和,具體的,加上\(e*dis\),這裡的相關節點指的是修改節點的祖先節點(我們顯然只需要記錄它對祖先的貢獻,根據點分樹的性質)
查詢就是列舉這個點與重心的在點分樹上的\(lca\),統計代價即可
因為點分樹的深度不超過\(\log n\),所以總複雜度是\(O(n log^2 n)\)
Code
#include<bits/stdc++.h>
#define ll long long
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
return x*f;
}
#define MN 100005
#define reg register
int n,m,en,hr[MN];
struct edge{int to,w,nex;}e[MN<<1];
inline void ins(int f,int t,int w)
{
e[++en]=(edge){t,w,hr[f]};hr[f]=en;
e[++en]=(edge){f,w,hr[t]};hr[t]=en;
}
bool vis[MN];
int rt,sm,siz[MN],dep[MN];
ll vsum,w[MN],val[MN];
struct Node{int id;ll dis;};
std::vector<Node> fa[MN],s[MN];
int HR[MN];
void pre_dfs(int x,int f)
{
reg int i;siz[x]=1;
for(i=hr[x];i;i=e[i].nex)if(!vis[e[i].to]&&(f^e[i].to))
pre_dfs(e[i].to,x),siz[x]+=siz[e[i].to];
}
void getroot(int x,int f)
{
reg int i;rt=x;
for(i=hr[x];i;i=e[i].nex)
if(!vis[e[i].to]&&(f^e[i].to)&&(siz[e[i].to]<<1)>=sm){getroot(e[i].to,x);break;}
}
void dfs(int x,int f,int ro)
{
fa[x].push_back((Node){ro,dep[x]});
reg int i;
for(i=hr[x];i;i=e[i].nex)if(!vis[e[i].to]&&(f^e[i].to))
dep[e[i].to]=dep[x]+e[i].w,dfs(e[i].to,x,ro);
}
inline void pre_work(int x=1,int f=0)
{
pre_dfs(x,f);
sm=siz[x];getroot(x,f);vis[rt]=true;
reg int i,j=rt;
s[f].push_back((Node){j,0LL});fa[j].push_back((Node){j,0LL});
HR[j]=x;
for(i=hr[rt];i;i=e[i].nex)
if(!vis[e[i].to]){dep[e[i].to]=e[i].w;dfs(e[i].to,j,j);pre_work(e[i].to,j);}
rt=j;
}
inline void Modify(int x,int y)
{
vsum+=y;val[x]+=y;reg int i,j=x,f,k;
for(i=fa[x].size()-1;~i;--i) w[fa[x][i].id]+=y;
for(i=fa[x].size()-2;~i;--i)
for(j=s[fa[x][i].id].size()-1;~j;--j)
if(s[fa[x][i].id][j].id==fa[x][i+1].id){s[fa[x][i].id][j].dis+=y*fa[x][i].dis;break;}
}
inline void upd(int x,int y){for(reg int i=fa[x].size()-1;~i;--i)w[fa[x][i].id]+=y;}
inline int Find(int x)
{
reg int ret=x,i,tmp;
for(i=s[x].size()-1;~i;--i)
if(w[s[x][i].id]*2>=vsum)
{
tmp=w[x]-w[s[x][i].id];
upd(HR[s[x][i].id],tmp);ret=Find(s[x][i].id);upd(HR[s[x][i].id],-tmp);
break;
}
return ret;
}
inline ll Query(int x)
{
reg ll ret=0;
reg int i,j,las=0;
for(i=fa[x].size()-1;~i;las=fa[x][i].id,--i)
{
ret+=fa[x][i].dis*val[fa[x][i].id];
for(j=s[fa[x][i].id].size()-1;~j;--j)
if(s[fa[x][i].id][j].id^las)
ret+=fa[x][i].dis*w[s[fa[x][i].id][j].id]+s[fa[x][i].id][j].dis;
}
return ret;
}
int main()
{
n=read();m=read();
register int i,x,y;
for(i=1;i<n;++i) x=read(),y=read(),ins(x,y,read());
pre_work();
while(m--)
{
x=read();y=read();Modify(x,y);
printf("%lld\n",Query(Find(rt)));
}
return 0;
}
Blog來自PaperCloud,未經允許,請勿轉載,TKS!