BZOJ3730 震波
题目大意
维护一棵树,每个点有点权,强制在线支持两种操作:
1、修改一个点的点权
2、查询距离一个点距离不超过$D$的点权值和。
题解
一道动态点分治模板好题。
先建出点分树,每个分治区重心以到重心距离为下标维护点权和,每次查询时由于要防止$x$同时对当前重心和当前重心的父节点有贡献,要容斥一下,在当前分治区记录当前分治区中的点对父亲分治区的贡献,每次要减去这个。
由于要执行$2(N+M)\log N$级别的修改或查询,所以动态开点的线段树由于常数过大很可能会$TLE$,所以我们最好使用树状数组。那么问题来了,树状数组如何开空间?
考虑到每个重心下标的最大值要么是分治区内的点到分治区重心的距离要么是分治区内的点到父亲分治区中心的距离,则有意义的最大值要么是分治区重心的子树的最大深度,要么是分治区的直径,为了保险起见,我开了$2$倍的最大子树$Size+1$。
还需注意树状数组的下标只能从$1$开始,而距离有可能是$0$,所以要特殊处理。
复杂度$2(N+M)\log^2N$为什么求$lca\space O(1)RMQ$不如树剖快啊。
不过可以采用分层记深度的方法无需预处理直接$O(1)$求距离,这里就不写了。
$O(1)RMQ$
#include<bits/stdc++.h> #define M 100010 #define link(a,b) nt[tmp]=fs[a],fs[a]=tmp,to[tmp++]=b using namespace std; namespace IO{ const int BS=(1<<23)+5; int Top=0; char Buffer[BS],OT[BS],*OS=OT,*HD,*TL,SS[20]; const char *fin=OT+BS-1; char Getchar(){if(HD==TL){TL=(HD=Buffer)+fread(Buffer,1,BS,stdin);} return (HD==TL)?EOF:*HD++;} void flush(){fwrite(OT,1,OS-OT,stdout);} void Putchar(char c){*OS++ =c;if(OS==fin)flush(),OS=OT;} void write(int x){ if(!x){Putchar('0');return;} if(x<0) x=-x,Putchar('-'); while(x) SS[++Top]=x%10,x/=10; while(Top) Putchar(SS[Top]+'0'),--Top; } int read(){ int nm=0,fh=1; char cw=Getchar(); for(;!isdigit(cw);cw=Getchar()) if(cw=='-') fh=-fh; for(;isdigit(cw);cw=Getchar()) nm=nm*10+(cw-'0'); return nm*fh; } } using namespace IO; int n,m,T,fa[M],dfn[M],tk[M],cnt,sz[M],val[M],dep[M],Sum,maxn,Root,sq[30]; int fs[M],nt[M<<1],to[M<<1],tmp,st[M<<1][19],tot,fst[M],lg[M<<1],mxs[M]; int *rt[M][2],*nw,sum[M*40]; bool vis[M]; void init(int x,int last){ dfn[x]=++cnt,tk[cnt]=x,dep[x]=dep[last]+1,st[fst[x]=++tot][0]=dfn[x]; for(int i=fs[x];i!=-1;i=nt[i]) if(to[i]!=last) init(to[i],x),st[++tot][0]=dfn[x]; } void gtsz(int x,int last){ sz[x]=1,mxs[x]=0; for(int i=fs[x];i!=-1;i=nt[i]){ if(vis[to[i]]||to[i]==last) continue; gtsz(to[i],x),sz[x]+=sz[to[i]]; } } void fdrt(int x,int last){ for(int i=fs[x];i!=-1;i=nt[i]){ if(vis[to[i]]||to[i]==last) continue; fdrt(to[i],x),mxs[x]=max(sz[to[i]],mxs[x]); } mxs[x]=max(mxs[x],Sum-sz[x]); if(mxs[x]<maxn) Root=x,maxn=mxs[x]; } void build(int x){ vis[x]=true,mxs[x]++,mxs[x]<<=1; rt[x][0]=nw,nw=nw+mxs[x]+1; rt[x][1]=nw,nw=nw+mxs[x]+1; for(int i=fs[x];i!=-1;i=nt[i]){ if(vis[to[i]]) continue; gtsz(to[i],x),maxn=n; Sum=sz[to[i]],fdrt(to[i],x),fa[Root]=x,build(Root); } } inline int lca(int x,int y){ if(fst[x]>fst[y]) swap(x,y); register int len=fst[y]-fst[x]+1,k; k=lg[len]; return tk[min(st[fst[x]][k],st[fst[y]-sq[k]+1][k])]; } #define dis(a,b) (dep[a]+dep[b]-(dep[lca(a,b)]<<1)) inline void add(int *x,int MAXN,int pos,int dt){for(int k=(++pos);k<=MAXN;k+=(k&-k)) x[k]+=dt;} inline int qry(int *x,int MAXN,int RS){int TT=0;for(int k=min(RS+1,MAXN);k>0;k-=(k&-k)) TT+=x[k]; return TT;} inline void ins(int x,int v,int num){ while(true){ add(rt[x][0],mxs[x],dis(x,v),num); if(!fa[x]) return; add(rt[x][1],mxs[x],dis(fa[x],v),num),x=fa[x]; } } inline int getans(int x,int v,int dt){ int res=0; while(true){ res+=qry(rt[x][0],mxs[x],dt-dis(v,x)); if(!fa[x]) return res; res-=qry(rt[x][1],mxs[x],dt-dis(v,fa[x])); x=fa[x]; } } int main(){ n=read(),T=read(),lg[0]=-1,sq[0]=1; nw=sum; for(int i=1;i<20;i++) sq[i]=(sq[i-1]<<1); for(int i=1;i<=n;i++) val[i]=read(),fs[i]=-1,vis[i]=false; for(int i=1;i<=(n<<1);i++) lg[i]=lg[i>>1]+1; for(int i=1;i<n;i++){int x=read(),y=read();link(x,y),link(y,x);} init(1,0),gtsz(1,0),fdrt(1,0),build(Root),cnt=0; for(int k=1;k<18;k++) for(int i=1;i<=tot-sq[k]+1;i++) st[i][k]=min(st[i][k-1],st[i+sq[k-1]][k-1]); for(int i=1;i<=n;i++) ins(i,i,val[i]); for(int tpe,x,y,ans=0;T;T--){ tpe=read(),x=read()^ans,y=read()^ans; if(tpe) ins(x,x,y-val[x]),val[x]=y; else write(ans=getans(x,x,y)),Putchar('\n'); }flush();return 0; }
树剖
#include<bits/stdc++.h> #define M 100010 #define link(a,b) nt[tmp]=fs[a],fs[a]=tmp,to[tmp++]=b using namespace std; namespace IO{ const int BS=(1<<22)+5; int Top=0; char Buffer[BS],OT[BS],*OS=OT,*HD,*TL,SS[20]; const char *fin=OT+BS-1; char Getchar(){if(HD==TL){TL=(HD=Buffer)+fread(Buffer,1,BS,stdin);} return (HD==TL)?EOF:*HD++;} void flush(){fwrite(OT,1,OS-OT,stdout);} void Putchar(char c){*OS++ =c;if(OS==fin)flush(),OS=OT;} void write(int x){ if(!x){Putchar('0');return;} if(x<0) x=-x,Putchar('-'); while(x) SS[++Top]=x%10,x/=10; while(Top) Putchar(SS[Top]+'0'),--Top; } int read(){ int nm=0,fh=1; char cw=Getchar(); for(;!isdigit(cw);cw=Getchar()) if(cw=='-') fh=-fh; for(;isdigit(cw);cw=Getchar()) nm=nm*10+(cw-'0'); return nm*fh; } } using namespace IO; int n,m,T,fa[M],cnt,sz[M],val[M],dep[M],Sum,maxn,Root,sq[30],tp[M],p[M]; int fs[M],nt[M<<1],to[M<<1],tmp,st[M<<1][19],tot,fst[M],lg[M<<1],mxs[M]; int *rt[M][2],*nw,sum[M*41],up[M<<1],dw[M<<1]; bool vis[M]; void init(int x,int last){ dep[x]=dep[p[x]=last]+(sz[x]=1),mxs[x]=0; for(int i=fs[x];i!=-1;i=nt[i]){ if(to[i]==last) continue; init(to[i],x),sz[x]+=sz[to[i]]; if(!mxs[x]||sz[mxs[x]]<sz[to[i]]) mxs[x]=to[i]; } } void gttop(int x,int dtp){ tp[x]=dtp; if(mxs[x]) gttop(mxs[x],dtp); for(int i=fs[x];i!=-1;i=nt[i]) if(to[i]!=p[x]&&to[i]!=mxs[x]) gttop(to[i],to[i]); } void gtsz(int x,int last){ sz[x]=1,mxs[x]=0; for(int i=fs[x];i!=-1;i=nt[i]){ if(vis[to[i]]||to[i]==last) continue; gtsz(to[i],x),sz[x]+=sz[to[i]]; } } void fdrt(int x,int last){ for(int i=fs[x];i!=-1;i=nt[i]){ if(vis[to[i]]||to[i]==last) continue; fdrt(to[i],x),mxs[x]=max(sz[to[i]],mxs[x]); } mxs[x]=max(mxs[x],Sum-sz[x]); if(mxs[x]<maxn) Root=x,maxn=mxs[x]; } void build(int x){ vis[x]=true,mxs[x]++,mxs[x]<<=1; rt[x][0]=nw,nw=nw+mxs[x]+1; rt[x][1]=nw,nw=nw+mxs[x]+1; for(int i=fs[x];i!=-1;i=nt[i]){ if(vis[to[i]]) continue; gtsz(to[i],x),maxn=n; Sum=sz[to[i]],fdrt(to[i],x),fa[Root]=x,build(Root); } } inline int lca(int x,int y){ while(tp[x]!=tp[y]) dep[tp[x]]>dep[tp[y]]?x=p[tp[x]]:y=p[tp[y]]; return dep[x]<dep[y]?x:y; } #define dis(a,b) (dep[a]+dep[b]-(dep[lca(a,b)]<<1)) inline void add(int *x,int MAXN,int pos,int dt){for(int k=(++pos);k<=MAXN;k=up[k]) x[k]+=dt;} inline int qry(int *x,int MAXN,int RS){int TT=0;for(int k=min(RS+1,MAXN);k>0;k=dw[k]) TT+=x[k]; return TT;} inline void ins(int x,int v,int num){ while(true){ add(rt[x][0],mxs[x],dis(x,v),num); if(!fa[x]) return; add(rt[x][1],mxs[x],dis(fa[x],v),num),x=fa[x]; } } inline int getans(int x,int v,int dt){ int res=0; while(true){ res+=qry(rt[x][0],mxs[x],dt-dis(v,x)); if(!fa[x]) return res; res-=qry(rt[x][1],mxs[x],dt-dis(v,fa[x])); x=fa[x]; } } int main(){ n=read(),T=read(),lg[0]=-1,sq[0]=1; nw=sum; for(int i=1;i<20;i++) sq[i]=(sq[i-1]<<1); for(int i=1;i<=n;i++) val[i]=read(),fs[i]=-1,vis[i]=false; for(int i=1;i<=(n<<1);i++) lg[i]=lg[i>>1]+1,up[i]=i+(i&-i),dw[i]=i-(i&-i); for(int i=1;i<n;i++){int x=read(),y=read();link(x,y),link(y,x);} init(1,0),gttop(1,1),gtsz(1,0),fdrt(1,0),build(Root),cnt=0; for(int i=1;i<=n;i++) ins(i,i,val[i]); for(int tpe,x,y,ans=0;T;T--){ tpe=read(),x=read()^ans,y=read()^ans; if(tpe) ins(x,x,y-val[x]),val[x]=y; else write(ans=getans(x,x,y)),Putchar('\n'); }flush();return 0; }