码迷,mamicode.com
首页 > 其他好文 > 详细

bzoj3924 [Zjoi2015]幻想乡战略游戏 点分树,动态点分

时间:2018-01-09 14:47:47      阅读:213      评论:0      收藏:0      [点我收藏+]

标签:mat   clu   ros   goto   swa   set   sigma   移动   接下来   

【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

第一行两个数n和Q分别表示树的点数和幽香操作的个数,其中点从1到n标号。 
接下来n-1行,每行三个正整数a,b,c,表示a和b之间有一条边权为c的边。 
接下来Q行,每行两个数u,e,表示幽香在点u上放了e单位个军队
(如果e<0,就相当于是幽香在u上减少了|e|单位个军队,说白了就是du←du+e)。
数据保证任何时刻每个点上的军队数量都是非负的。 
1<=c<=1000, 0<=|e|<=1000, n<=10^5, Q<=10^5
对于所有数据,这个树上所有点的度数都不超过20
N,Q>=1

Output

 对于幽香的每个操作,输出操作完成以后,每天的最小花费,也即如果幽香选择最优的补给点进行补给时的花费。 

Sample Input

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

Sample Output

0
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 }

 

bzoj3924 [Zjoi2015]幻想乡战略游戏 点分树,动态点分

标签:mat   clu   ros   goto   swa   set   sigma   移动   接下来   

原文地址:https://www.cnblogs.com/fengzhiyuan/p/8251038.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!