【BZOJ3924】[Zjoi2015]幻想乡战略游戏 动态树分治
【BZOJ3924】[Zjoi2015]幻想乡战略游戏
Description
傲娇少女幽香正在玩一个非常有趣的战略类游戏,本来这个游戏的地图其实还不算太大,幽香还能管得过来,但是不知道为什么现在的网游厂商把游戏的地图越做越大,以至于幽香一眼根本看不过来,更别说和别人打仗了。 在打仗之前,幽香现在面临一个非常基本的管理问题需要解决。 整个地图是一个树结构,一共有n块空地,这些空地被n-1条带权边连接起来,使得每两个点之间有一条唯一的路径将它们连接起来。在游戏中,幽香可能在空地上增加或者减少一些军队。同时,幽香可以在一个空地上放置一个补给站。 如果补给站在点u上,并且空地v上有dv个单位的军队,那么幽香每天就要花费dv×dist(u,v)的金钱来补给这些军队。由于幽香需要补给所有的军队,因此幽香总共就要花费为Sigma(Dv*dist(u,v),其中1<=V<=N)的代价。其中dist(u,v)表示u个v在树上的距离(唯一路径的权和)。 因为游戏的规定,幽香只能选择一个空地作为补给站。在游戏的过程中,幽香可能会在某些空地上制造一些军队,也可能会减少某些空地上的军队,进行了这样的操作以后,出于经济上的考虑,幽香往往可以移动他的补给站从而省一些钱。但是由于这个游戏的地图是在太大了,幽香无法轻易的进行最优的安排,你能帮帮她吗? 你可以假定一开始所有空地上都没有军队。
Input
Output
对于幽香的每个操作,输出操作完成以后,每天的最小花费,也即如果幽香选择最优的补给点进行补给时的花费。
Sample Input
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
Sample Output
1
4
5
6
题意:给你一棵树,多次改变一个点的权值或询问所有点到当前带权重心的带权距离是多少。
题解:先考虑如何求所有点到一个点的带权距离。我们先沿用树形DP的思想,令:
s1:每个点的子树中的所有点到该点的带权距离。
s2:每个点的子树中的所有点到该点的父亲的带权距离。
s3:每个点的子树中的所有点的权值和。
然后就可以统计答案了,从每个点一直向上爬,沿途记录所有点的s1,减去重复的部分的s2,再加上不足的距离dis*s3,细节还是自己搞吧~时间复杂度logn。
由于题目保证每个点的度数<=20,那么我们可以采用如下方法找重心:从分治结构的root开始,枚举它的所有出边(原树中的出边),如果往那个方向走得到的总距离比当前的优,则走到那条出边对应的(分治结构中的)儿子上,否则当前点就是重心。总复杂度O(nlog2n*20)。
#include <cstdio> #include <cstring> #include <iostream> #include <vector> using namespace std; const int maxn=100010; typedef long long ll; int n,m,tot,rt,root,mn,cnt; int to[maxn<<1],next[maxn<<1],head[maxn],siz[maxn],fa[maxn],vis[maxn]; int pos[maxn],Log[maxn<<1]; ll md[19][maxn<<1],dep[maxn],val[maxn<<1]; ll s1[maxn],s2[maxn],s3[maxn]; //s1:点*边,s2:点,s3到父亲的 vector<int> c1[maxn],c2[maxn]; inline int rd() { int ret=0,f=1; char gc=getchar(); while(gc<'0'||gc>'9') {if(gc=='-')f=-f; gc=getchar();} while(gc>='0'&&gc<='9') ret=ret*10+gc-'0',gc=getchar(); return ret*f; } void add(int a,int b,int c) { to[cnt]=b,val[cnt]=c,next[cnt]=head[a],head[a]=cnt++; } void dfs(int x,int fa) { md[0][++pos[0]]=dep[x],pos[x]=pos[0]; for(int i=head[x];i!=-1;i=next[i]) if(to[i]!=fa) dep[to[i]]=dep[x]+val[i],dfs(to[i],x),md[0][++pos[0]]=dep[x]; } void getrt(int x,int fa) { siz[x]=1; int i,tmp=0; for(i=head[x];i!=-1;i=next[i]) if(!vis[to[i]]&&to[i]!=fa) getrt(to[i],x),siz[x]+=siz[to[i]],tmp=max(tmp,siz[to[i]]); tmp=max(tmp,tot-siz[x]); if(tmp<mn) mn=tmp,rt=x; } void solve(int x) { vis[x]=1; for(int i=head[x];i!=-1;i=next[i]) if(!vis[to[i]]) tot=siz[to[i]],mn=1<<30,getrt(to[i],x),fa[rt]=x,c1[x].push_back(rt),c2[x].push_back(to[i]),solve(rt); } ll lca(int a,int b) { a=pos[a],b=pos[b]; if(a>b) swap(a,b); int k=Log[b-a+1]; return min(md[k][a],md[k][b-(1<<k)+1]); } ll dis(int a,int b) { return dep[a]+dep[b]-2*lca(a,b); } void updata(int x,int y,ll z) { s1[x]+=dis(x,y)*z,s2[x]+=z; if(!fa[x]) return ; s3[x]+=dis(fa[x],y)*z; updata(fa[x],y,z); } ll getdis(int x,int y) { ll ret=s1[x]; if(fa[x]) ret+=getdis(fa[x],y)-s3[x]+(s2[fa[x]]-s2[x])*dis(fa[x],y); return ret; } int moveroot(int x) { ll now=getdis(x,x); for(int i=0,j=0;i<(int)c1[x].size();i++,j++) if(getdis(c2[x][i],c2[x][i])<now) return moveroot(c1[x][j]); return x; } int main() { n=rd(),m=rd(); int i,j,a,b,c; memset(head,-1,sizeof(head)); for(i=1;i<n;i++) a=rd(),b=rd(),c=rd(),add(a,b,c),add(b,a,c); dep[1]=1,dfs(1,0); for(i=2;i<=2*n-1;i++) Log[i]=Log[i>>1]+1; for(j=1;(1<<j)<=2*n-1;j++) for(i=1;i+(1<<j)-1<=2*n-1;i++) md[j][i]=min(md[j-1][i],md[j-1][i+(1<<(j-1))]); tot=n,mn=1<<30,getrt(1,0),root=rt,solve(rt); for(i=1;i<=m;i++) { a=rd(),b=rd(); updata(a,a,b); rt=moveroot(root); printf("%lld\n",getdis(rt,rt)); } return 0; }//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
| 欢迎来原网站坐坐! >原文链接<