这题可以用线段树做,不过正解恐怕是动态点分治?(点分树)
简单介绍下动态点分治的概念:在点分治的过程中,一般我们面对的问题都是静态的。如果涉及到修改这类的操作,我们就希望找到我们是如何处理到当前的修改点的,换而言之,我们希望记录下点分治的过程,这样可以通过爬点分树等操作消除影响。
对于每个节点我们保存这个子树的dv的总和已经把该节点作为点的答案值
这样对于修改能在$O(logn)的时间内解决
简要看下题意:询问树的带权重心,加修改。
如果不加修改,找到树的重心就是一个树形dp的傻题。
寻找答案的时候,我们可以发现,如果现在节点的子树dv和*2大于总节点,那么向那个方向过去一定比原方案好。
考虑如何处理修改?
我们先从根节点开始,若发现答案在某棵子树时,我们考虑如何使其儿子节点的答案转变为整个树的答案,可以发现把除这个子树外的所有节点可以缩成一个节点并连在这棵子树上,然后就可以一直这样做下去,找到操作之后再把这些撤销。
下面是咸鱼给出的一些实现细节:
1.跟虚树一样(不知道的可以百度“世界树”),这也是要在第二棵树上对原树进行统计,所以一定千万小心不要弄混,我学到的办法是对原树进行封装,这样即使不小心访问错了他也会给你提示。
2.对树进行欧拉序遍历可以利用rmq在$O(1)$查询LCA,这点也是特别好懂的。(只会树剖的蒟蒻瑟瑟发抖)
代码总共113行,应该是可读性比较好的。
zyz的那个码风清奇根本不能看啊QAQ
应该是目前比较能看的点分树代码之一吧。
#include<bits/stdc++.h> #define N 100010 typedef long long ll; using namespace std; int n,m,x,y,z,size[N],tot; int head[N],sum,rt,f[N],vis[N],par[N],cnt,lg[N<<2]; ll dis1[N],dis2[N],sumv[N]; struct Edge{int u,v,next,w;}G[2*N]; void Addedge(int u,int v,int w){ G[++tot].u=u;G[tot].v=v;G[tot].w=w;G[tot].next=head[u];head[u]=tot; } struct Orinal_Tree{ int head[N],cnt,tot; int st[N<<2][21],dis[N],tpos[N<<1]; struct edge{int u,v,next,w;}E[2*N]; inline void addedge(int u,int v,int w){ E[++tot].u=u;E[tot].v=v;E[tot].w=w;E[tot].next=head[u];head[u]=tot; E[++tot].u=v;E[tot].v=u;E[tot].w=w;E[tot].next=head[v];head[v]=tot; } inline int getdis(int u,int v){ if(tpos[u]>tpos[v])swap(u,v); int k=lg[tpos[v]-tpos[u]+1]; return dis[u]+dis[v]-2*min(st[tpos[u]][k],st[tpos[v]-(1<<k)+1][k]); } void dfs(int u,int fa){ st[++cnt][0]=dis[u];tpos[u]=cnt; for(int i=head[u];i;i=E[i].next){ int v=E[i].v;if(v==fa)continue; dis[v]=dis[u]+E[i].w; dfs(v,u); st[++cnt][0]=dis[u]; } } inline void initrmq(){ memset(tpos,0,sizeof(tpos));cnt=0;tot=0;dis[1]=0; dfs(1,0); for(int j=1;(1<<j)<=cnt;j++) for(int i=1;i+(1<<j)-1<=cnt&&i<=cnt;i++) st[i][j]=min(st[i][j-1],st[i+(1<<(j-1))][j-1]); } }T; void getroot(int u,int fa){ size[u]=1;f[u]=0; for(int i=T.head[u];i;i=T.E[i].next){ int v=T.E[i].v;if(vis[v]||v==fa)continue; getroot(v,u);size[u]+=size[v]; f[u]=max(f[u],size[v]); } f[u]=max(f[u],sum-size[u]); if(f[u]<f[rt])rt=u; } void work(int u,int fa){ vis[u]=1;par[u]=fa;//printf("%d ",u);puts("!!!"); for(int i=T.head[u];i;i=T.E[i].next){ int v=T.E[i].v;if(vis[v])continue; sum=size[v];f[0]=size[v];rt=0; getroot(v,0);Addedge(u,rt,v); work(rt,u); } } inline void ins(int u,int val){ sumv[u]+=val; for(int i=u;par[i];i=par[i]){ int dist=T.getdis(par[i],u); dis1[par[i]]+=(ll)dist*val; dis2[i]+=(ll)dist*val; sumv[par[i]]+=val; } } inline ll calc(int u){ ll ans=dis1[u]; for(int i=u;par[i];i=par[i]){ int dist=T.getdis(par[i],u); ans+=dis1[par[i]]-dis2[i]; ans+=dist*(sumv[par[i]]-sumv[i]); } return ans; } ll query(int u){ ll ans=calc(u);//printf("%lld ",ans); for(int i=head[u];i;i=G[i].next){ ll tmp=calc(G[i].w); if(tmp<ans)return query(G[i].v); } return ans; } inline int read(){ int f=1,x=0;char ch; do{ch=getchar();if(ch=='-')f=-1;}while(ch<'0'||ch>'9'); do{x=x*10+ch-'0';ch=getchar();}while(ch>='0'&&ch<='9'); return f*x; } void init(){ memset(dis1,0,sizeof(dis1)); memset(dis2,0,sizeof(dis2)); memset(sumv,0,sizeof(sumv)); lg[0]=-1; for (int i=1;i<(N<<2);i++)lg[i]=lg[i>>1]+1; n=read();m=read();//printf("%d %d ",n,m); for (int i=1;i<n;i++) x=read(),y=read(),z=read(),T.addedge(x,y,z); } int main(){ init();T.initrmq();sum=n;f[0]=n; rt=0;getroot(1,0); int LastOrder=rt;work(rt,0);rt=LastOrder; //for(int i=1;i<=20;i++)//printf("%d ",T.head[i]);puts(""); for(int i=1;i<=m;i++){ x=read();y=read();ins(x,y); printf("%lld ",query(rt)); } return 0; }