[ZJOI2015]幻想乡战略游戏(点分树)
题意自己看。。。
思路
没想到今(昨)天刷着刷着点分治的水题,就刷出来了一个点分树。。。
然后就疯狂地找题解,代码,最后终于把它给弄懂了。
点分树——动态点分治,对于此题来说,我们设u为当前的补给站位置,v是它的一个儿子。同时设dis(i,j)为树上i点到j点的距离。cnti为以i为根的子树中d(也就是军队数)的总量
我们把补给站从u转移到v(假设u是树的根),答案的变化为dis(u,v)*(cntu-cntv)-dis(u,v)*cntv=dis(u,v)*(cntu-2*cntv)
所以当2*cntv>cntu把补给站转移v会使答案变优,一直这样操作下去,直到不管如何转移都无法使答案变优,当前点就是答案。
我们对于每一次修改都维护一下然后默认当前点为补给站且为当前树的根,然后像刚才一样转移补给站,就有了这个题的大体思路。
但这样肯定会爆炸啊(每次只会转移一步,复杂度和深度有关)
所以我们就可以用到点分树了。
我们利用点分治的方法(找重心作为根节点,然后递归处理子树)重新构建一颗树,称为分治树。
举个例子,比如说样例
(左为原树右为分治树)
这样我们就得到一颗深度为log的树。
这样我们转移就不会因为深度太大而GG了。
但是,这样一来的话因为树的结构不一样了,补给站的转移就会很难。因为一个点在分治树的儿子在原树中不一定和它直接相连
(之后我们的补给站的转移都是在分治树上进行的)
但还是有办法解决的。我们记录这这些东西。dis(i,j)为i,j在原树的距离。cnt[i]为分治树中以i为根的子树中有多少个d,sumi为分治树中以i为根的子树中所有的点j的dis(i,j)*dj之和,sumch(i,j)记录i点第j个儿子的sum。
我们现在讨论从u转移到它分治树中的一个子节点v,v是原来这颗子树中的重心但在原树中并不一定和u之间相连,我们设原树中直接和u相连的点是w。
因为把补给站转到v的时候要把v作为整棵树的根,只要把v所在子树之外的所有信息扔到w中然后递归就可以了。那怎么维护w的信息呢?
我们记录在v处记录下w和w与u这条边的权值c,然后我们在cntw处加上cnt[u]-cnt[v],然后在sumw处加上sum[u]-sumch(i,v)+(cnt[u]-cnt[v])*c;
最后答案就取sum[当前点]就行了。
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 #include<cmath> 5 #include<algorithm> 6 #include<vector> 7 using namespace std; 8 #define int long long 9 const int N=1e5+1100; 10 struct son{int v,rv,w;}; 11 struct node{int u,c,w;}; 12 struct pre{int to,w;}; 13 vector<son>ch[N]; 14 vector<node>stack; 15 vector<pre>f[N]; 16 vector<int>sumch[N]; 17 int num,head[N]; 18 int g[N],root,size[N],all,vis[N]; 19 int realroot,sum[N],cnt[N],n,m; 20 struct edge{ 21 int to,nxt,w; 22 }e[N*2]; 23 void add_edge(int u,int v,int w){ 24 num++; 25 e[num].nxt=head[u]; 26 e[num].to=v; 27 e[num].w=w; 28 head[u]=num; 29 } 30 int read(){ 31 int sum=0,f=1;char ch=getchar(); 32 while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} 33 while(ch>='0'&&ch<='9'){sum=sum*10+ch-'0';ch=getchar();} 34 return sum*f; 35 } 36 void getroot(int u,int f){ 37 g[u]=0;size[u]=1; 38 for(int i=head[u];i;i=e[i].nxt){ 39 int v=e[i].to; 40 if(v==f||vis[v])continue; 41 getroot(v,u); 42 g[u]=max(g[u],size[v]); 43 size[u]+=size[v]; 44 } 45 g[u]=max(g[u],all-size[u]); 46 if(g[u]<g[root])root=u; 47 } 48 void dfs(int u,int fa,int top,int w){ 49 pre x;x.to=top;x.w=w; 50 f[u].push_back(x); 51 size[u]=1; 52 for(int i=head[u];i;i=e[i].nxt){ 53 int v=e[i].to; 54 if(vis[v]||v==fa)continue; 55 dfs(v,u,top,w+e[i].w); 56 size[u]+=size[v]; 57 } 58 } 59 void work(int u){ 60 pre y;y.w=0;y.to=u; 61 f[u].push_back(y); 62 vis[u]=1; 63 for(int i=head[u];i;i=e[i].nxt){ 64 int v=e[i].to; 65 if(vis[v]==1)continue; 66 dfs(v,0,u,e[i].w); 67 root=0,all=size[v]; 68 getroot(v,0); 69 son x;x.v=root;x.rv=v;x.w=e[i].w; 70 ch[u].push_back(x); 71 work(root); 72 } 73 } 74 void update(int u,int c,int w){ 75 for(int i=0;i<f[u].size();i++){ 76 int v=f[u][i].to; 77 int L=f[u][i].w; 78 cnt[v]+=c; 79 sum[v]+=w+c*L; 80 if(i!=f[u].size()-1) 81 for(int j=0;j<ch[v].size();j++){ 82 if(ch[v][j].v==f[u][i+1].to)sumch[v][j]+=w+c*L; 83 } 84 } 85 } 86 int check(){ 87 int now=realroot,mx; 88 stack.clear(); 89 while(now){ 90 mx=0; 91 for(int i=1;i<ch[now].size();i++) 92 if(cnt[ch[now][i].v]>cnt[ch[now][mx].v])mx=i; 93 if(ch[now].size()==0||cnt[ch[now][mx].v]*2<=cnt[now]){ 94 int ans=sum[now]; 95 for(int i=0;i<stack.size();i++) 96 update(stack[i].u,stack[i].c,stack[i].w); 97 return ans; 98 } 99 int v=ch[now][mx].v; 100 node x; 101 x.u=ch[now][mx].rv;x.c=-(cnt[now]-cnt[v]); 102 x.w=-(sum[now]-sumch[now][mx]+(cnt[now]-cnt[v])*ch[now][mx].w); 103 stack.push_back(x); 104 update(x.u,-x.c,-x.w); 105 now=v; 106 } 107 } 108 signed main(){ 109 n=read();m=read(); 110 for(int i=1;i<n;i++){ 111 int u=read(),v=read(),w=read(); 112 add_edge(u,v,w);add_edge(v,u,w); 113 } 114 g[0]=n+10;root=0;all=n; 115 getroot(1,0);realroot=root; 116 work(root); 117 for(int i=1;i<=n;i++)sumch[i]=vector<int>(ch[i].size(),0); 118 while(m--){ 119 int x=read(),y=read(); 120 update(x,y,0); 121 printf("%lld\n",check()); 122 } 123 return 0; 124 }