【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:每个点的子树中的所有点的权值和。
可以先计算出每个重心的S
那S -- T 会改变什么
Δ=T-S=(∑valu->S-∑valv->T)*e.v,发现随着移动,属于S的会更多,不会少,所有
发现这个函数是一定会从负到正,到正以后没有意义了,因为答案是需要最小
所以就这样了。
这道题目在当时省选的时候是不可以暴力转移根的,但也有一些分数,
bzoj上是直接可以过的,
但是这里该怎么办呢,就需要建立点分树。
点分树上是log的,所以在点分树上跳就只需要log次,所以复杂度就有了保证。
1 #include<cstring> 2 #include<cmath> 3 #include<iostream> 4 #include<algorithm> 5 #include<cstdio> 6 #include<vector> 7 8 #define ll long long 9 #define N 100007 10 using namespace std; 11 inline int read() 12 { 13 int x=0,f=1;char ch=getchar(); 14 while(ch<‘0‘||ch>‘9‘){if (ch==‘-‘)f=-1;ch=getchar();} 15 while(ch>=‘0‘&&ch<=‘9‘){x=(x<<1)+(x<<3)+ch-‘0‘;ch=getchar();} 16 return x*f; 17 } 18 19 int n,m,tot,rt,root,mn; 20 int cnt,hed[N],nxt[N*2],rea[N*2]; 21 int pos[N],Log[N*2],fa[N],vis[N],siz[N]; 22 ll md[20][N*2],dep[N],val[N*2]; 23 ll s1[N],s2[N],s3[N]; 24 vector<int>c1[N],c2[N]; 25 26 void add(int u,int v,int w) 27 { 28 nxt[++cnt]=hed[u]; 29 hed[u]=cnt; 30 rea[cnt]=v; 31 val[cnt]=w; 32 } 33 void dfs(int u,int fa) 34 { 35 md[0][++pos[0]]=dep[u],pos[u]=pos[0]; 36 for(int i=hed[u];i!=-1;i=nxt[i]) 37 { 38 int v=rea[i]; 39 if (v==fa)continue; 40 dep[v]=dep[u]+val[i],dfs(v,u),md[0][++pos[0]]=dep[u]; 41 } 42 } 43 void get_root(int u,int fa) 44 { 45 siz[u]=1; 46 int res=0; 47 for (int i=hed[u];i!=-1;i=nxt[i]) 48 { 49 int v=rea[i]; 50 if (v==fa||vis[v])continue; 51 get_root(v,u); 52 siz[u]+=siz[v],res=max(res,siz[v]); 53 } 54 res=max(res,tot-siz[u]); 55 if (res<mn) mn=res,rt=u; 56 } 57 void solve(int u) 58 { 59 vis[u]=1; 60 for (int i=hed[u];i!=-1;i=nxt[i]) 61 { 62 int v=rea[i]; 63 if (vis[v])continue; 64 tot=siz[v],mn=1<<30; 65 get_root(v,u),fa[rt]=u; 66 c1[u].push_back(rt); 67 c2[u].push_back(v); 68 solve(rt); 69 } 70 } 71 ll lca(int a,int b) 72 { 73 a=pos[a],b=pos[b]; 74 if (a>b) swap(a,b); 75 int k=Log[b-a+1]; 76 return min(md[k][a],md[k][b-(1<<k)+1]); 77 } 78 ll dis(int a,int b) 79 { 80 return dep[a]+dep[b]-2*lca(a,b); 81 } 82 void modify(int x,int y,ll z) 83 { 84 s1[x]+=dis(x,y)*z,s2[x]+=z; 85 if (!fa[x])return; 86 s3[x]+=dis(fa[x],y)*z; 87 modify(fa[x],y,z); 88 } 89 ll get_dis(int x,int y) 90 { 91 ll res=s1[x]; 92 if (fa[x])res+=get_dis(fa[x],y)-s3[x]+(s2[fa[x]]-s2[x])*dis(fa[x],y); 93 return res; 94 } 95 int goto_root(int u) 96 { 97 ll now=get_dis(u,u); 98 for (int i=0,j=0;i<(int)c1[u].size();i++,j++) 99 if (get_dis(c2[u][i],c2[u][i])<now)return goto_root(c1[u][j]); 100 return u; 101 } 102 int main() 103 { 104 memset(hed,-1,sizeof(hed)); 105 n=read(),m=read(); 106 for (int i=1;i<n;i++) 107 { 108 int x=read(),y=read(),z=read(); 109 add(x,y,z),add(y,x,z); 110 } 111 dep[1]=1,dfs(1,0); 112 for (int i=2;i<=2*n-1;i++) 113 Log[i]=Log[i>>1]+1; 114 for (int i=1;(1<<i)<=(2*n-1);i++) 115 for (int j=1;j+(1<<i)-1<=2*n-1;j++) 116 md[i][j]=min(md[i-1][j],md[i-1][j+(1<<(i-1))]); 117 tot=n,mn=1<<30; 118 get_root(1,0),root=rt,solve(rt); 119 for (int i=1;i<=m;i++) 120 { 121 int x=read(),y=read(); 122 modify(x,x,y); 123 rt=goto_root(root);//每次从原树中继续分治。 124 //cout<<"rt="<<rt<<endl; 125 printf("%lld\n",get_dis(rt,rt)); 126 } 127 }