BZOJ 3924: [Zjoi2015]幻想乡战略游戏(动态点分治)

题目描述

傲娇少女幽香正在玩一个很是有趣的战略类游戏,原本这个游戏的地图其实还不算太大,幽香还能管得过来,可是不知道为何如今的网游厂商把游戏的地图越作越大,以致于幽香一眼根本看不过来,更别说和别人打仗了。ios

在打仗以前,幽香如今面临一个很是基本的管理问题须要解决。 整个地图是一个树结构,一共有n块空地,这些空地被n-1条带权边链接起来,使得每两个点之间有一条惟一的路径将它们链接起来。git

在游戏中,幽香可能在空地上增长或者减小一些军队。同时,幽香能够在一个空地上放置一个补给站。 若是补给站在点u上,而且空地v上有dv个单位的军队,那么幽香天天就要花费dv*dist(u,v)的金钱来补给这些军队。数组

因为幽香须要补给全部的军队,所以幽香总共就要花费为Sigma(Dv*dist(u,v),其中1<=V<=N)的代价。其中dist(u,v)表示u个v在树上的距离(惟一路径的权和)。spa

由于游戏的规定,幽香只能选择一个空地做为补给站。在游戏的过程当中,幽香可能会在某些空地上制造一些军队,也可能会减小某些空地上的军队,进行了这样的操做之后,出于经济上的考虑,幽香每每能够移动他的补给站从而省一些钱。code

可是因为这个游戏的地图是在太大了,幽香没法轻易的进行最优的安排,你能帮帮她吗? 你能够假定一开始全部空地上都没有军队。blog

输入输出格式

输入格式:递归

第一行两个数n和Q分别表示树的点数和幽香操做的个数,其中点从1到n标号。 接下来n-1行,每行三个正整数a,b,c,表示a和b之间有一条边权为c的边。 接下来Q行,每行两个数u,e,表示幽香在点u上放了e单位个军队(若是e<0,就至关因而幽香在u上减小了|e|单位个军队,说白了就是du←du+e)。数据保证任什么时候刻每一个点上的军队数量都是非负的。游戏

输出格式:get

对于幽香的每一个操做,输出操做完成之后,天天的最小花费,也即若是幽香选择最优的补给点进行补给时的花费。string

输入输出样例

输入样例#1:  复制
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
输出样例#1:  复制
0
1
4
5
6

说明

对于全部数据,1<=c<=1000, 0<=|e|<=1000, n<=10^5, Q<=10^5 很是神奇的是,对于全部数据,这棵树上的点的度数都不超过20,且N,Q>=1

题解

  噩梦……真的噩梦……我快被幽香给玩死了……

  用这道题来理解动态淀粉质点分治的(由于当初捉迷藏那题直接学岛娘的括号序列……就没去再打一遍……),然而题解大佬们都直接默认咱们已经会动态点分而后直接上……结果我昨天看了一个晚上才弄懂……而后今天又花了一个早上码完,交上去居然1A了难以想象

  先说一下什么是动态点分治吧。对于通常的点分治,由于咱们每一次都找出重心,因此每一次递归往左右子树找的时候深度不会超过$O(log n)$层。可是若是有了修改操做怎么办呢?每修改一次点分一次么?那怕是得T飞

  咱们来看一下这道题目,它的每一次修改,更改的只有点权,树的结构是没有变化的(若是有的话怕是只能上LCT了……),也就是说,每一次点分的时候找到的重心是不会改变的。那么,咱们可不能够把点分治每一层的重心给连成一棵树呢?由于点分的递归层数只有$O(log n)$层,因此这棵点分树的深度也是$O(log n)$的

  那么考虑一下在每个点分树的节点维护什么东西,对于每个节点,咱们维护他的子树中的全部信息,也就是在原树中它被选为重心时的那个子树的全部信息。修改的时候,只要从一个点开始在点分树里往上跳,并不断更新信息便可。查询一个点的时候,点分树里跳,不断考虑与父亲之间的贡献就行了。

  上面那一段看不懂也不要紧,由于我只是在口胡,伪装本身已经很懂动态点分的样子

  那么咱们具体来分析一下这道题目咱们要维护什么

  题目要求使$\sum d_v*dis(u,v)$最小,其中$d_v$为$v$点的点权,$dis(u,v)$为原树中$u,v$两点的距离。题目要求就是求带权重心。假设咱们当前已经选定了点$v$为答案,那么考虑它的一个子节点$w$,若是把补给站从点$v$转移到$w$,那么$w$的全部子树内的点到补给站的距离少了$dis(v,w)$,而其余全部点到补给站的距离多了$dis(v,w)$。咱们假设$sum_v[v]$为$v$的子树内的点权和,那么答案总共的变化量是$$dis(u,v)*(sum_v[v]-sum_v[w]-sum_v[w])$$

  不难发现,当$sum_v[w]*2>sum_v[v]$的时候,答案的变化量是小于零的,也就是说代价减少,能够变得更优。并且,知足这样条件的点$w$最多只有一个

  那么咱们能够每一次选定一个点,而后看看往他的哪一个子树走更优,若是没有说明他本身就已是最优的了。这样不断下去确定能找到答案。

  可是因为原图多是一条链,要怎么作才能保证复杂度呢?咱们选择在点分树上走,每一次都跳到它下一层的重心,这样能够保证层数最多只有$O(log n)$

  而后考虑如何维护答案。不难发现,对于点分树上一个点$v$,它的子树中的点就是在点分治时它被选为重心时的那棵树上的点。考虑点分树上的一对父子$u,v$,咱们设$sum_a[v]$表示$v$的子树内的全部点到他的代价之和,$sum_b[v]$为$v$的子树内的全部点到$v$点父亲(也就是点$u$)的距离之和,$sum_v[v]$仍是表示子树的点权之和。那么咱们设答案已经选定为点$v$,加上$u$的不包括$v$的子树后答案就是$sum_a[v]-sum_b[v]+sum_a[u]+sum_v[u]*dis(u,v)$。因而只要在点分树上不断找父亲并合并,就能够知道答案了

  而后咱们只要能在修改时维护好这三个数组就能够了!!!

  至于修改时如何维护呢?咱们修改一个点以后,而后不断在点分树上往父节点跳,并不断更新便可。查询的时候也是,不断跳并合并答案

  而后又新学会了一招,用$RMQ O(1)$查询$LCA$(只会倍增和树剖的我瑟瑟发抖),总时间复杂度$O(nlog^2n)$

  1 //minamoto
  2 #include<cstdio>
  3 #include<iostream>
  4 #include<cstring>
  5 #define ll long long
  6 #define N 100005
  7 #define inf 0x3f3f3f3f
  8 #define rint register int
  9 using namespace std;
 10 #define getc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
 11 char buf[1<<21],*p1=buf,*p2=buf;
 12 template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
 13 inline int read(){
 14     #define num ch-'0'
 15     char ch;bool flag=0;int res;
 16     while(!isdigit(ch=getc()))
 17     (ch=='-')&&(flag=true);
 18     for(res=num;isdigit(ch=getc());res=res*10+num);
 19     (flag)&&(res=-res);
 20     #undef num
 21     return res;
 22 }
 23 char sr[1<<21],z[20];int C=-1,Z;
 24 inline void Ot(){fwrite(sr,1,C+1,stdout),C=-1;}
 25 inline void print(ll x){
 26     if(C>1<<20)Ot();if(x<0)sr[++C]=45,x=-x;
 27     while(z[++Z]=x%10+48,x/=10);
 28     while(sr[++C]=z[Z],--Z);sr[++C]='\n';
 29 }
 30 struct G{
 31     int head[N],Next[N<<1],edge[N<<1],ver[N<<1],tot;
 32     G(){tot=0;memset(head,0,sizeof(head));}
 33     inline void add(int u,int v,int e){
 34         ver[++tot]=v,Next[tot]=head[u],head[u]=tot,edge[tot]=e;
 35     }
 36 }T1,T2;
 37 int n,q,st[N<<1][18],logn[N<<1],bin[25],tp;
 38 ll sum,ans,d[N],dis1[N],dis2[N],sumv[N];
 39 int dfn[N],num;
 40 void dfs1(int u,int fa){
 41     st[dfn[u]=++num][0]=d[u];
 42     for(int i=T1.head[u];i;i=T1.Next[i]){
 43         int v=T1.ver[i];
 44         if(v==fa) continue;
 45         d[v]=d[u]+T1.edge[i],dfs1(v,u),st[++num][0]=d[u];
 46     }
 47 }
 48 inline ll LCA(int a,int b){
 49     if(dfn[a]>dfn[b]) a^=b^=a^=b;
 50     int k=logn[dfn[b]-dfn[a]+1];
 51     return min(st[dfn[a]][k],st[dfn[b]-bin[k]+1][k])<<1;
 52 }
 53 inline ll dis(int a,int b){return d[a]+d[b]-LCA(a,b);}
 54 int sz[N],son[N],size,rt,fa[N];bool vis[N];
 55 void dfs2(int u,int fa){
 56     sz[u]=1,son[u]=0;
 57     for(int i=T1.head[u];i;i=T1.Next[i]){
 58         int v=T1.ver[i];
 59         if(vis[v]||v==fa) continue;
 60         dfs2(v,u),sz[u]+=sz[v],cmax(son[u],sz[v]);
 61     }
 62     cmax(son[u],size-sz[u]);
 63     if(son[u]<son[rt]) rt=u;
 64 }
 65 void dfs3(int u){
 66     vis[u]=true;
 67     for(int i=T1.head[u];i;i=T1.Next[i]){
 68         int v=T1.ver[i];
 69         if(vis[v]) continue;
 70         rt=0,size=sz[v],son[0]=n+1;
 71         dfs2(v,0),T2.add(u,rt,v),fa[rt]=u,dfs3(rt);
 72     }
 73 }
 74 inline void update(int u,int val){
 75     sumv[u]+=val;
 76     for(int p=u;fa[p];p=fa[p]){
 77         ll dist=dis(fa[p],u)*val;
 78         dis1[fa[p]]+=dist;
 79         dis2[p]+=dist;
 80         sumv[fa[p]]+=val;
 81     }
 82 }
 83 inline ll calc(int u){
 84     ll ans=dis1[u];
 85     for(int p=u;fa[p];p=fa[p]){
 86         ll dist=dis(fa[p],u);
 87         ans+=dis1[fa[p]]-dis2[p];
 88         ans+=dist*(sumv[fa[p]]-sumv[p]);
 89     }
 90     return ans;
 91 }
 92 ll query(int u){
 93     ll ans=calc(u);
 94     for(int i=T2.head[u];i;i=T2.Next[i]){
 95         ll tmp=calc(T2.edge[i]);
 96         if(tmp<ans) return query(T2.ver[i]);
 97     }
 98     return ans;
 99 }
100 void init(){
101     n=read(),q=read();
102     bin[0]=1,logn[0]=-1;
103     for(rint i=1;i<=20;++i) bin[i]=bin[i-1]<<1;
104     while(bin[tp+1]<=(n<<1)) ++tp;
105     for(rint i=1;i<=(n<<1);++i) logn[i]=logn[i>>1]+1;
106     for(rint i=1;i<n;++i){
107         rint u=read(),v=read(),e=read();
108         T1.add(u,v,e),T1.add(v,u,e);
109     }
110     dfs1(1,0),rt=0,son[0]=n+1,size=n,dfs2(1,0);
111     for(rint j=1;j<=tp;++j)
112     for(rint i=1;i+bin[j]-1<=(n<<1);++i)
113     st[i][j]=min(st[i][j-1],st[i+bin[j-1]][j-1]);
114 }
115 int main(){
116     init();
117     int LastOrder=rt;dfs3(rt);
118     while(q--){
119         int x=read(),y=read();update(x,y);
120         print(query(LastOrder));
121     }
122     Ot();
123     return 0;
124 }
相关文章
相关标签/搜索