题意:维护一棵带点权和边权的树,支持加点,改点权,询问子树和,询问两点距离
我怎么开始刷水题了?(雾
离线把树建出来,用线段树维护dfs序,用倍增求lca,用于求点对距离
初始时让除了根以外的节点权值都为$0$,修改或加点时再把点权改成相应的值
子树和就是区间和
#include<stdio.h> #define ll long long struct edge{ int to,nex,w; }e[600010]; struct ask{ int c[5]; }q[600010]; int h[600010],fa[600010][20],in[600010],dep[600010],siz[600010],tot,M; ll dis[600010],s[2400010]; void add(int a,int b,int c){ tot++; e[tot].to=b; e[tot].w=c; e[tot].nex=h[a]; h[a]=tot; } void dfs(int x){ in[x]=++tot; siz[x]=1; for(int i=h[x];i;i=e[i].nex){ if(e[i].to!=fa[x][0]){ fa[e[i].to][0]=x; dis[e[i].to]=dis[x]+e[i].w; dep[e[i].to]=dep[x]+1; dfs(e[i].to); siz[x]+=siz[e[i].to]; } } } void swap(int&x,int&y){x^=y^=x^=y;} int lca(int x,int y){ int i; if(dep[x]<dep[y])swap(x,y); for(i=19;i>=0;i--){ if(dep[fa[x][i]]>=dep[y])x=fa[x][i]; } if(x==y)return x; for(i=19;i>=0;i--){ if(fa[x][i]!=fa[y][i]){ x=fa[x][i]; y=fa[y][i]; } } return fa[x][0]; } int main(){ int n,m,i,j,ov,l,r; ll res; char ss[10]; scanf("%d%d",&m,&ov); n=1; for(i=1;i<=m;i++){ scanf("%s",ss); if(ss[0]==‘A‘){ n++; q[i].c[0]=1; scanf("%d%d%d%d",q[i].c+1,q[i].c+2,q[i].c+3,q[i].c+4); add(q[i].c[3],q[i].c[1],q[i].c[4]); } if(ss[0]==‘C‘){ q[i].c[0]=2; scanf("%d%d",q[i].c+1,q[i].c+2); } if(ss[0]==‘Q‘&&ss[5]==‘1‘){ q[i].c[0]=3; scanf("%d",q[i].c+1); } if(ss[0]==‘Q‘&&ss[5]==‘2‘){ q[i].c[0]=4; scanf("%d%d",q[i].c+1,q[i].c+2); } } tot=0; dep[1]=1; dfs(1); for(j=1;j<20;j++){ for(i=1;i<=n;i++)fa[i][j]=fa[fa[i][j-1]][j-1]; } for(M=1;M<n+2;M<<=1); for(i=in[1]+M;i;i>>=1)s[i]=ov; for(i=1;i<=m;i++){ if(q[i].c[0]==1||q[i].c[0]==2){ j=in[q[i].c[1]]+M; for(s[j]=q[i].c[2],j>>=1;j;j>>=1)s[j]=s[j<<1]+s[j<<1|1]; } if(q[i].c[0]==3){ res=0; l=in[q[i].c[1]]+M-1; r=l+siz[q[i].c[1]]+1; for(;l^r^1;l>>=1,r>>=1){ if(~l&1)res+=s[l^1]; if(r&1)res+=s[r^1]; } printf("%lld\n",res); } if(q[i].c[0]==4)printf("%lld\n",dis[q[i].c[1]]+dis[q[i].c[2]]-(dis[lca(q[i].c[1],q[i].c[2])]<<1)); } }