對於一個點,要求出它到所有點的帶權距離和,只需記錄下樹分治的結構然後查詢即可。
修改$O(\log n)$,查詢$O(\log n)$。
到所有點帶權距離和最小的點顯然是這棵樹的帶權重心。
以1號點為根,考慮一條從父親x到孩子y的邊:
若y子樹內權值和>=總權值和-y子樹內權值和,即2*y子樹內權值和>=總權值和,則重心在y的子樹裡,否則不在。
所以重心一定是深度最大的滿足2*子樹內權值和>=總權值和的點。
可以發現重心及其祖先都滿足2*子樹內權值和>=總權值和,而這些點在DFS序上從左往右深度遞增。
所以樹鏈剖分後用線段樹維護DFS序,修改單點點權相當於一條樹鏈整體加上一個數。
查詢重心時直接線上段樹上二分即可。
修改$O(\log^2n)$,查詢$O(\log n)$。
#include<cstdio> typedef long long ll; const int N=100010,M=2000000,T=262145; int n,m,i,x,y,z; int g[N],nxt[N<<1],v[N<<1],w[N<<1],ok[N<<1],ed=1,son[N],f[N],all,now,cnt,value[N]; int size[N],heavy[N],top[N],loc[N],seq[N],dfn; int G[N],NXT[M],V[2][M],W[M],ED,tag[T]; ll val[T],sw[N],sdw[N],sew[N],sedw[N]; inline void read(int&a){ char c;bool f=0;a=0; while(!((((c=getchar())>='0')&&(c<='9'))||(c=='-'))); if(c!='-')a=c-'0';else f=1; while(((c=getchar())>='0')&&(c<='9'))(a*=10)+=c-'0'; if(f)a=-a; } inline void add(int x,int y,int z){v[++ed]=y,w[ed]=z,nxt[ed]=g[x],ok[ed]=1,g[x]=ed;} inline void ADD(int x,int y,int z,int w){V[0][++ED]=y;V[1][ED]=z;W[ED]=w;NXT[ED]=G[x];G[x]=ED;} void findroot(int x,int pre){ son[x]=1;f[x]=0; for(int i=g[x];i;i=nxt[i])if(ok[i]&&v[i]!=pre){ findroot(v[i],x); son[x]+=son[v[i]]; if(son[v[i]]>f[x])f[x]=son[v[i]]; } if(all-son[x]>f[x])f[x]=all-son[x]; if(f[x]<f[now])now=x; } void dfs(int x,int pre,int dis){ ADD(x,now,cnt,dis); for(int i=g[x];i;i=nxt[i])if(ok[i]&&v[i]!=pre)dfs(v[i],x,dis+w[i]); } void solve(int x){ int i; for(i=g[x];i;i=nxt[i])if(ok[i])++cnt,dfs(v[i],x,w[i]); for(i=g[x];i;i=nxt[i])if(ok[i])ok[i^1]=0,f[0]=all=son[v[i]],findroot(v[i],now=0),solve(now); } void dfs1(int x,int y){ size[x]=1;f[x]=y; for(int i=g[x];i;i=nxt[i])if(v[i]!=y){ dfs1(v[i],x);size[x]+=size[v[i]]; if(size[v[i]]>size[heavy[x]])heavy[x]=v[i]; } } void dfs2(int x,int y){ top[x]=y;seq[loc[x]=++dfn]=x; if(heavy[x])dfs2(heavy[x],y); for(int i=g[x];i;i=nxt[i])if(v[i]!=heavy[x]&&v[i]!=f[x])dfs2(v[i],v[i]); } inline void add1(int x,int p){val[x]+=p,tag[x]+=p;} inline void pb(int x){if(tag[x])add1(x<<1,tag[x]),add1(x<<1|1,tag[x]),tag[x]=0;} void change(int x,int a,int b,int c,int d,int p){ if(c<=a&&b<=d){add1(x,p);return;} pb(x); int mid=(a+b)>>1; if(c<=mid)change(x<<1,a,mid,c,d,p); if(d>mid)change(x<<1|1,mid+1,b,c,d,p); val[x]=val[x<<1]>val[x<<1|1]?val[x<<1]:val[x<<1|1]; } inline int getroot(){ int x=1,a=1,b=n,mid; while(a<b){ pb(x),mid=(a+b)>>1; if(val[x<<1|1]*2>=val[1])a=mid+1,x=x<<1|1;else b=mid,x<<=1; } return seq[a]; } inline void modify(int x,int y){ value[x]+=y; for(int i=G[x];i;i=NXT[i]){ sw[V[0][i]]+=y,sdw[V[0][i]]+=(ll)W[i]*y; sew[V[1][i]]+=y,sedw[V[1][i]]+=(ll)W[i]*y; } while(top[x]!=1)change(1,1,n,loc[top[x]],loc[x],y),x=f[top[x]]; change(1,1,n,1,loc[x],y); } inline ll query(int x){ ll t=sdw[x]; for(int i=G[x];i;i=NXT[i])t+=(sw[V[0][i]]-sew[V[1][i]]+value[V[0][i]])*W[i]+sdw[V[0][i]]-sedw[V[1][i]]; return t; } int main(){ read(n),read(m); for(i=1;i<n;i++)read(x),read(y),read(z),add(x,y,z),add(y,x,z); f[0]=all=n;findroot(1,now=0);solve(now); dfs1(1,0),dfs2(1,1); while(m--)read(x),read(y),modify(x,y),printf("%lld\n",query(getroot())); return 0; }