BZOJ 3924 ZJOI2015 幻想乡战略游戏 树链剖分
题目链接:https://www.luogu.org/problemnew/show/P3345(bzoj权限题)
题意概述:动态维护树的上所有点到这棵树的带权重心的距离和。N,Q<=100000.
分析:
首先考虑一个性质,对于任意一个点i来说,如果存在一个儿子j使得sz[j]*2>tot,那么i一定不是树的重心,并且重心应该在j的子树中,否则重心就可以是i。(这里必须是事先积累的知识,证明简单,略)
于是就可以考虑一个暴力辣!每次从根出发按照上面的条件进行查找,一旦发现一个点没有儿子满足上面的性质,那么这个点就应该是一个重心。
但是这样一次次走很慢。注意到我们找的是深度最大的j满足sz[j]*2>tot(满足条件的j一定在一条链上面),这里有很多的做法,但是考虑到我们计算答案的时候可能用到树剖,所以我们选择在DFS序上面操作,维护区间的sz最大值,直接在线段树上来二分查找,一次查找当前重心的复杂度是O(logn)。
关于答案的计算,我们发现对于点i为重心的时候,其答案分成两部分,一个是i的子树,一个是i的祖先。我们单独计算每个祖先不包含i的其余子树的贡献,全部加起来就是这次的答案。
通过计算,一次的答案可以表示为:
sum[1]+d[i]*(tot-sz[i])-sum{ f(j) | j为i到1路径上的节(包括i)) }
f(i)=sz[i]*(d[i]-d[fa[i]])+(sz[fa[i]]-sz[i])*d[fa[i]]
sum[i]表示i的子树中的点到i的带权距离和,sz[i]表示i的子树中所有点的权值和,d[i]表示i到1的路径长度,tot表示所有点的权值和。
维护:树剖,维护所有的sz和f以及sz的最大值,重链顶端的点不要去维护,每一次跳到的时候单独计算其f(因为没有办法同时维护一个点所有的儿子的f)。每次修改一个点的权值之后,其到1路径上所有点的sz都会改变,我们发现这些点的f值会发生变化,同时在更新中我们到达的所有的点的重儿子的f也会发生变化,记得更新一下。
时间复杂度O(N+Q*logN^2)
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<cstdlib> 5 #include<algorithm> 6 #include<cmath> 7 #include<queue> 8 #include<set> 9 #include<map> 10 #include<vector> 11 #include<cctype> 12 using namespace std; 13 typedef long long LL; 14 15 int N,Q; 16 struct segment_tree{ 17 static const int maxn=100005; 18 struct edge{ int to,next,w; }E[maxn<<1]; 19 int first[maxn],np,n,dist[maxn],_sz[maxn],fa[maxn],son[maxn],top[maxn]; 20 int dfs_clock,l[maxn],a[maxn],sz[maxn]; 21 int rt,np2,lc[maxn<<1],rc[maxn<<1],mx[maxn<<1],flag[maxn<<1],tot; 22 LL g[maxn<<1],sum; 23 segment_tree(){ sum=np=np2=rt=0; } 24 void add_edge(int u,int v,int w){ 25 E[++np]=(edge){v,first[u],w}; 26 first[u]=np; 27 } 28 void DFS1(int i,int f,int l){ 29 fa[i]=f,dist[i]=l; 30 _sz[i]=1,sz[i]=0; 31 for(int p=first[i];p;p=E[p].next){ 32 int j=E[p].to; 33 if(j==f) continue; 34 DFS1(j,i,l+E[p].w); 35 _sz[i]+=_sz[j]; 36 if(_sz[j]>_sz[son[i]]) son[i]=j; 37 } 38 } 39 void DFS2(int i,int f,int tp){ 40 top[i]=tp,l[i]=++dfs_clock; 41 a[dfs_clock]=i; 42 if(son[i]) DFS2(son[i],i,tp); 43 for(int p=first[i];p;p=E[p].next){ 44 int j=E[p].to; 45 if(j==f||j==son[i]) continue; 46 DFS2(j,i,j); 47 } 48 } 49 void pushup(int now){ 50 mx[now]=max(mx[lc[now]],mx[rc[now]]); 51 g[now]=g[lc[now]]+g[rc[now]]; 52 } 53 void pushdown(int now,int L,int R){ 54 if(!flag[now]) return; 55 int m=L+R>>1; 56 if(top[a[L]]==a[L]) g[lc[now]]+=1ll*flag[now]*(dist[a[m]]-dist[a[L]]); 57 else g[lc[now]]+=1ll*flag[now]*(dist[a[m]]-dist[fa[a[L]]]); 58 if(top[a[m+1]]==a[m+1]) g[rc[now]]+=1ll*flag[now]*(dist[a[R]]-dist[a[m+1]]); 59 else g[rc[now]]+=1ll*flag[now]*(dist[a[R]]-dist[a[m]]); 60 mx[lc[now]]+=flag[now],mx[rc[now]]+=flag[now]; 61 flag[lc[now]]+=flag[now],flag[rc[now]]+=flag[now]; 62 flag[now]=0; 63 } 64 void build(int &now,int L,int R){ 65 now=++np2,lc[now]=rc[now]=0; 66 g[now]=mx[now]=flag[now]=0; 67 if(L==R) return; 68 int m=L+R>>1; 69 build(lc[now],L,m); build(rc[now],m+1,R); 70 } 71 int query_p(int now,int L,int R){ 72 if(L==R) return a[L]; 73 pushdown(now,L,R); 74 int m=L+R>>1; 75 if(mx[rc[now]]*2>tot) return query_p(rc[now],m+1,R); 76 return query_p(lc[now],L,m); 77 } 78 void query(int now,int L,int R,int pos){ 79 if(L==R){ 80 sz[a[L]]+=flag[now],flag[now]=0; 81 return; 82 } 83 pushdown(now,L,R); 84 int m=L+R>>1; 85 if(pos<=m) query(lc[now],L,m,pos); 86 else query(rc[now],m+1,R,pos); 87 } 88 LL query_g(int now,int L,int R,int A,int B){ 89 if(A<=L&&R<=B) return g[now]; 90 pushdown(now,L,R); 91 int m=L+R>>1; 92 if(B<=m) return query_g(lc[now],L,m,A,B); 93 if(A>m) return query_g(rc[now],m+1,R,A,B); 94 return query_g(lc[now],L,m,A,B)+query_g(rc[now],m+1,R,A,B); 95 } 96 void update1(int now,int L,int R,int A,int B,int delt){ 97 if(A<=L&&R<=B){ 98 mx[now]+=delt,flag[now]+=delt; 99 if(top[a[L]]==a[L]) g[now]+=1ll*delt*(dist[a[R]]-dist[a[L]]); 100 else g[now]+=1ll*delt*(dist[a[R]]-dist[fa[a[L]]]); 101 return; 102 } 103 pushdown(now,L,R); 104 int m=L+R>>1; 105 if(B<=m) update1(lc[now],L,m,A,B,delt); 106 else if(A>m) update1(rc[now],m+1,R,A,B,delt); 107 else update1(lc[now],L,m,A,B,delt),update1(rc[now],m+1,R,A,B,delt); 108 pushup(now); 109 } 110 void update2(int now,int L,int R,int pos,int delt){ 111 if(L==R){ 112 sz[a[L]]+=flag[now],flag[now]=0; 113 if(top[a[L]]!=a[L]) g[now]+=1ll*delt*dist[fa[a[L]]]; 114 return; 115 } 116 pushdown(now,L,R); 117 int m=L+R>>1; 118 if(pos<=m) update2(lc[now],L,m,pos,delt); 119 else update2(rc[now],m+1,R,pos,delt); 120 pushup(now); 121 } 122 void update(int p,int delt){ 123 tot+=delt,sum+=1ll*delt*dist[p]; 124 while(p){ 125 update1(rt,1,n,l[top[p]],l[p],delt); 126 if(son[p]) update2(rt,1,n,l[son[p]],delt); 127 p=fa[top[p]]; 128 } 129 } 130 LL ans(){ 131 int p=query_p(rt,1,n); 132 query(rt,1,n,l[p]); 133 LL re=sum+1ll*dist[p]*(tot-sz[p]); 134 while(p){ 135 re-=query_g(rt,1,n,l[top[p]],l[p]); 136 p=top[p]; 137 query(rt,1,n,l[p]); query(rt,1,n,l[fa[p]]); 138 re-=1ll*sz[p]*(dist[p]-dist[fa[p]])+1ll*(sz[fa[p]]-sz[p])*dist[fa[p]]; 139 p=fa[p]; 140 } 141 return re; 142 } 143 }st; 144 145 bool nega; 146 void _scanf(int &x) 147 { 148 x=0,nega=0; 149 char ch=getchar(); 150 while((ch<'0'||ch>'9')&&ch!='-') ch=getchar(); 151 if(ch=='-') nega=1,ch=getchar(); 152 while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar(); 153 if(nega) x=-x; 154 } 155 int out_cnt,out[20]; 156 void _printf(LL x) 157 { 158 out[++out_cnt]=x%10,x/=10; 159 while(x) out[++out_cnt]=x%10,x/=10; 160 while(out_cnt) putchar('0'+out[out_cnt--]); 161 putchar('\n'); 162 } 163 void data_in() 164 { 165 _scanf(N);_scanf(Q); st.n=N; 166 int x,y,z; 167 for(int i=1;i<N;i++){ 168 _scanf(x);_scanf(y);_scanf(z); 169 st.add_edge(x,y,z); st.add_edge(y,x,z); 170 } 171 } 172 void work() 173 { 174 int u,e; 175 st.DFS1(1,0,0); 176 st.DFS2(1,0,1); 177 st.build(st.rt,1,st.n); 178 for(int i=1;i<=Q;i++){ 179 _scanf(u);_scanf(e); 180 st.update(u,e); 181 _printf(st.ans()); 182 } 183 } 184 int main() 185 { 186 data_in(); 187 work(); 188 return 0; 189 }