bzoj3730. 震波
动态点分治
考虑从每一个"块"里找到距离k范围内的点的和
为了去重,
每个x维护两个线段树:(都是关于自己分治树子树的点)
1.下标为距离x的距离,权值为val的
2.下标为距离x的分治树father的距离,权值为val
这样,统计的时候
计算分治树祖先块的时候,把从自己那里出来的块的东西再减去
注意:
1.点分治nowrt是全局变量,所以divi这一层用tmp额外记录一下,否则回溯回来再下去的时候,nowrt就不是这一层的了
2.不能某个祖先的dis大于k,就直接break了。因为到分治树祖先的距离没有单调性!!!!
代码:
#include<bits/stdc++.h> #define il inline #define reg register int #define numb (ch^'0') #define mid ((l+r)>>1) using namespace std; typedef long long ll; il void rd(int &x){ char ch;bool fl=false; while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true); for(x=numb;isdigit(ch=getchar());x=x*10+numb); (fl==true)&&(x=-x); } namespace Miracle{ const int N=100000+5; int n,m; struct node{ int nxt,to; }e[2*N]; int hd[N],cnt; void add(int x,int y){ e[++cnt].nxt=hd[x]; e[cnt].to=y; hd[x]=cnt; } int fa[N]; int sz[N]; int mx[N]; int nowsz; int tmprt; int rt[N][2];//0 is myself ; 1 is disfather int nowrt; struct tr{ int ls,rs; int sum; }t[N*50]; int tot; int dist[N][20]; int dep[N]; int val[N]; int vis[N]; void pushup(int x){ t[x].sum=t[t[x].ls].sum+t[t[x].rs].sum; } void upda(int &x,int l,int r,int p,int v){ if(!x) x=++tot; if(l==r){ t[x].sum+=v; return; } if(p<=mid) upda(t[x].ls,l,mid,p,v); else upda(t[x].rs,mid+1,r,p,v); pushup(x); } int query(int x,int l,int r,int L,int R){//no dis=0 (myself) if(L>R) return 0; if(L<=l&&r<=R){ return t[x].sum; } int ret=0; if(L<=mid) ret+=query(t[x].ls,l,mid,L,R); if(mid<R) ret+=query(t[x].rs,mid+1,r,L,R); return ret; } void dfs1(int x,int f,int d,int dis){//find nowrt sz[x]=1; mx[x]=0; dep[x]=d; if(d!=1){ upda(tmprt,0,n,dis,val[x]); } for(reg i=hd[x];i;i=e[i].nxt){ int y=e[i].to; if(vis[y]||y==f) continue; dfs1(y,x,d,dis+1); sz[x]+=sz[y]; mx[x]=max(mx[x],sz[y]); } mx[x]=max(mx[x],nowsz-sz[x]); if(mx[x]<=nowsz/2&&(nowsz-sz[x])<=nowsz/2){ nowrt=x; } } void dfs2(int x,int f,int d,int dis){//find dis from nowrt sz[x]=1; dep[x]=d; dist[x][d]=dis; upda(rt[nowrt][0],0,n,dis,val[x]); for(reg i=hd[x];i;i=e[i].nxt){ int y=e[i].to; if(vis[y]||y==f) continue; dfs2(y,x,d,dis+1); sz[x]+=sz[y]; } } void divi(int x,int d,int f){ //mxsz=0; // cout<<" x "<<x<<" : "<<d<<" "<<f<<endl; nowrt=0; tmprt=0; dfs1(x,0,d,1); fa[nowrt]=f; rt[nowrt][1]=tmprt; // cout<<" nowrt "<<nowrt<<endl; dfs2(nowrt,0,d,0); vis[nowrt]=1; int tmp=nowrt; for(reg i=hd[tmp];i;i=e[i].nxt){ int y=e[i].to; if(!vis[y]){ nowsz=sz[y]; divi(y,d+1,tmp); } } } int wrk0(int x,int k){ int ret=0; ret+=query(rt[x][0],0,n,0,k); int tmp=x; while(fa[x]){ int y=fa[x]; // if(k-dist[tmp][dep[y]]>=0) ret+=query(rt[y][0],0,n,0,k-dist[tmp][dep[y]]); // else break; ret-=query(rt[x][1],0,n,0,k-dist[tmp][dep[y]]); x=y; } return ret; } void wrk1(int x,int v){ int tmp=x; int c=v-val[x]; val[x]=v; upda(rt[x][0],0,n,0,c); while(fa[x]){ int y=fa[x]; upda(rt[x][1],0,n,dist[tmp][dep[y]],c); upda(rt[y][0],0,n,dist[tmp][dep[y]],c); x=y; } } int main(){ rd(n);rd(m); for(reg i=1;i<=n;++i) rd(val[i]); int x,y; for(reg i=1;i<n;++i){ rd(x);rd(y); add(x,y);add(y,x); } nowsz=n; divi(1,1,0); // cout<<" toot "<<tot<<endl; int op,k; int lasans=0; while(m--){ rd(op); rd(x);rd(k); x^=lasans;k^=lasans; if(op==0) printf("%d\n",lasans=wrk0(x,k)); else wrk1(x,k); } return 0; } } signed main(){ // freopen("data.in","r",stdin); // freopen("my.out","w",stdout); Miracle::main(); return 0; } /* Author: *Miracle* Date: 2019/2/6 10:11:29 */
总结:
动态点分治往往为了去重,经常考虑x对fa的贡献
相当于在分治树的往fa走的边上造了一个线段树
(每个点一个,每个边一个,一共n+n-1个)