P6329 【模板】点分树 | 震波
P6329 【模板】点分树 | 震波
来补点分树模板的题解了:
先明确一下点分树的定义:又很多个重心构成的一棵树,且树上的层数关系对应重心的大小
那么我们为什么要建这一颗树呢:因为我们要处理多组询问并且又修改.
然后点分树的建树方式其实在定义中就几乎给出了,就是在求重心时将新老重心连一条边.
然后我们开始思考本题:“所有与 x 距离不超过 k 的城市都将受到影响”:
我们不难想到对于一个重心lca,在它的子树下对答案的贡献(将目标节点设为y)就是:
其中,
节点ancx表示的是一个既是lca的儿子,又是x的先祖(也可能是本身)的节点.
然后这题的思路就比较明确了:维护两颗动态开点线段树,对于一个点x:
T1维护
T2维护
其中fa为x在点分树上的父亲
然后对于每个操作,在点分树上不断向上跳并执行对应操作就好了
然后这题就愉快的做完了
我是绝对不会告诉你我卡了一下午常的 😦(((
Code
#include<bits/stdc++.h> const int N=2e5+5; const int inf=1e9; const int lg=17; using namespace std; int to[N<<1],nxt[N<<1]; int n,m,cent,e_cnt; int f[N][lg+5],fa[N],siz[N],mx[N],dep[N],a[N]; int head[N]; int read() { int res=0; char c=getchar(); while(c<'0'||'9'<c){c=getchar();} while('0'<=c&&c<='9'){res=(res<<3)+(res<<1)+(c-'0');c=getchar();} return res; } inline void add(int x,int y) { e_cnt++; to[e_cnt]=y;nxt[e_cnt]=head[x]; head[x]=e_cnt; } void dfs(int u,int ff) { f[u][0]=ff; for(register int i=head[u];i;i=nxt[i]) { register int v=to[i]; if(v==ff)continue; dep[v]=dep[u]+1; dfs(v,u); } return ; } inline int LCA(int x,int y) { if(dep[x]<dep[y])swap(x,y); for(int i=lg;~i;i--) { if(dep[f[x][i]]>=dep[y])x=f[x][i]; } if(x==y)return x; for(int i=lg;~i;i--) { if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i]; } return f[x][0]; } inline int Dis(int x,int y) { int lca=LCA(x,y); return dep[x]+dep[y]-(dep[lca]<<1); } int vis[N]; void get_cent(int u,int ff,int tot) { siz[u]=1;mx[u]=0; for(register int i=head[u];i;i=nxt[i]) { register int v=to[i]; if(v==ff||vis[v])continue; get_cent(v,u,tot); siz[u]+=siz[v]; mx[u] = (mx[u]>siz[v] ? mx[u] : siz[v]); } mx[u] = (mx[u] > tot-siz[u] ? mx[u] : tot-siz[u]); cent = mx[cent] < mx[u] ? cent : u; } void divide(int u,int ff,int tot) { vis[u]=1; for(register int i=head[u];i;i=nxt[i]) { register int v=to[i]; if(v==ff||vis[v])continue; int son_tot = (siz[v]<siz[u] ? siz[v] : (tot-siz[u])); cent=0; get_cent(v,u,son_tot); fa[cent]=u; divide(cent,u,son_tot); } } struct Segment_Tree{ int rt[N]; struct Node{ int ls,rs,val; }t[N*40]; int cnt=0; inline void push_up(int x) { t[x].val=t[t[x].ls].val+t[t[x].rs].val; } inline void upd(int &x,int l,int r,int pos,int w) { if(!x)x=++cnt; if(l==r) { t[x].val+=w; return ; } int mid=l+r>>1; if(pos<=mid)upd(t[x].ls,l,mid,pos,w); if(mid<pos) upd(t[x].rs,mid+1,r,pos,w); push_up(x); } inline int query(int x,int l,int r,int L,int R) { if(!x)return 0; if(L<=l&&r<=R) { return t[x].val; } int mid=l+r>>1; int res=0; if(L<=mid)res+=query(t[x].ls,l,mid,L,R); if(mid<R)res+=query(t[x].rs,mid+1,r,L,R); return res; } }T1,T2; inline void upd(int x,int w) { int now=x; while(now) { int dis=Dis(now,x); T1.upd(T1.rt[now],0,n-1,dis,w); if(fa[now]){dis=Dis(fa[now],x);T2.upd(T2.rt[now],0,n-1,dis,w);} now=fa[now]; } } inline int query(int x,int k) { int now=x,pre=0,res=0; while(now) { int dis=Dis(x,now); if(dis>k) { pre=now;now=fa[now];continue; } res+=T1.query(T1.rt[now],0,n-1,0,k-dis); if(pre)res-=T2.query(T2.rt[pre],0,n-1,0,k-dis); pre=now;now=fa[now]; } return res; } void work() { cin>>n>>m; for(int i=1;i<=n;i++) { a[i]=read(); } for(int i=1,x,y;i<n;i++) { x=read(); y=read(); add(x,y); add(y,x); } dfs(1,0); for(int u=1;u<=n;u++){for(int i=1;i<=lg;i++){f[u][i]=f[f[u][i-1]][i-1];}} int ans=0; mx[0]=inf; get_cent(1,0,n); divide(1,0,n); for(register int u=1;u<=n;u++){upd(u,a[u]);} for(register int i=1,opt,x,y;i<=m;i++) { opt=read(); x=read(); y=read(); x^=ans; y^=ans; if(opt==0) { ans=query(x,y); printf("%d\n",ans); } else { upd(x,y-a[x]); a[x]=y; } } } int main() { //freopen("P6329.in","r",stdin); //freopen("P6329.out","w",stdout); work(); return 0; } ```# [P6329 【模板】点分树 | 震波](https://www.luogu.com.cn/problem/P6329) 来补点分树模板的题解了: 先明确一下点分树的定义:又很多个重心构成的一棵树,且树上的层数关系对应重心的大小 那么我们为什么要建这一颗树呢:因为我们要处理多组询问并且又修改. 然后点分树的建树方式其实在定义中就几乎给出了,就是在求重心时将新老重心连一条边. 然后我们开始思考本题:“所有与 x 距离不超过 k 的城市都将受到影响”: 我们不难想到对于一个重心lca,在它的子树下对答案的贡献(将目标节点设为y)就是: $\sum{y\in S_{lca}}[dis_{y,lca}\le k-dis_{lca,x}]*val_y -\sum{y\in S_{ancx}}[dis_{y,lca}\le k-dis_{lca,x}]*val_y$ 其中,$S_{lca}$表示在lca的子树内,$S_{ancx}$类似. 节点ancx表示的是一个既是lca的儿子,又是x的先祖(也可能是本身)的节点. 然后这题的思路就比较明确了:维护两颗动态开点线段树,对于一个点x: T1维护$sum_y|$ $\forall y \in S_{x}$ $ dis_{x,y}\in[l,r]$ T2维护$sum_y|$ $\forall y \in S_{x}$ $ dis_{fa,y}\in[l,r]$ 其中fa为x在**点分树**上的父亲 然后对于每个操作,在点分树上不断向上跳并执行对应操作就好了 然后这题就愉快的做完了 ~~我是绝对不会告诉你我卡了一下午常的~~ [:((((](https://www.luogu.com.cn/record/list?pid=P6329&user=508086) # Code ```cpp #include<bits/stdc++.h> const int N=2e5+5; const int inf=1e9; const int lg=17; using namespace std; int to[N<<1],nxt[N<<1]; int n,m,cent,e_cnt; int f[N][lg+5],fa[N],siz[N],mx[N],dep[N],a[N]; int head[N]; int read() { int res=0; char c=getchar(); while(c<'0'||'9'<c){c=getchar();} while('0'<=c&&c<='9'){res=(res<<3)+(res<<1)+(c-'0');c=getchar();} return res; } inline void add(int x,int y) { e_cnt++; to[e_cnt]=y;nxt[e_cnt]=head[x]; head[x]=e_cnt; } void dfs(int u,int ff) { f[u][0]=ff; for(register int i=head[u];i;i=nxt[i]) { register int v=to[i]; if(v==ff)continue; dep[v]=dep[u]+1; dfs(v,u); } return ; } inline int LCA(int x,int y) { if(dep[x]<dep[y])swap(x,y); for(int i=lg;~i;i--) { if(dep[f[x][i]]>=dep[y])x=f[x][i]; } if(x==y)return x; for(int i=lg;~i;i--) { if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i]; } return f[x][0]; } inline int Dis(int x,int y) { int lca=LCA(x,y); return dep[x]+dep[y]-(dep[lca]<<1); } int vis[N]; void get_cent(int u,int ff,int tot) { siz[u]=1;mx[u]=0; for(register int i=head[u];i;i=nxt[i]) { register int v=to[i]; if(v==ff||vis[v])continue; get_cent(v,u,tot); siz[u]+=siz[v]; mx[u] = (mx[u]>siz[v] ? mx[u] : siz[v]); } mx[u] = (mx[u] > tot-siz[u] ? mx[u] : tot-siz[u]); cent = mx[cent] < mx[u] ? cent : u; } void divide(int u,int ff,int tot) { vis[u]=1; for(register int i=head[u];i;i=nxt[i]) { register int v=to[i]; if(v==ff||vis[v])continue; int son_tot = (siz[v]<siz[u] ? siz[v] : (tot-siz[u])); cent=0; get_cent(v,u,son_tot); fa[cent]=u; divide(cent,u,son_tot); } } struct Segment_Tree{ int rt[N]; struct Node{ int ls,rs,val; }t[N*40]; int cnt=0; inline void push_up(int x) { t[x].val=t[t[x].ls].val+t[t[x].rs].val; } inline void upd(int &x,int l,int r,int pos,int w) { if(!x)x=++cnt; if(l==r) { t[x].val+=w; return ; } int mid=l+r>>1; if(pos<=mid)upd(t[x].ls,l,mid,pos,w); if(mid<pos) upd(t[x].rs,mid+1,r,pos,w); push_up(x); } inline int query(int x,int l,int r,int L,int R) { if(!x)return 0; if(L<=l&&r<=R) { return t[x].val; } int mid=l+r>>1; int res=0; if(L<=mid)res+=query(t[x].ls,l,mid,L,R); if(mid<R)res+=query(t[x].rs,mid+1,r,L,R); return res; } }T1,T2; inline void upd(int x,int w) { int now=x; while(now) { int dis=Dis(now,x); T1.upd(T1.rt[now],0,n-1,dis,w); if(fa[now]){dis=Dis(fa[now],x);T2.upd(T2.rt[now],0,n-1,dis,w);} now=fa[now]; } } inline int query(int x,int k) { int now=x,pre=0,res=0; while(now) { int dis=Dis(x,now); if(dis>k) { pre=now;now=fa[now];continue; } res+=T1.query(T1.rt[now],0,n-1,0,k-dis); if(pre)res-=T2.query(T2.rt[pre],0,n-1,0,k-dis); pre=now;now=fa[now]; } return res; } void work() { cin>>n>>m; for(int i=1;i<=n;i++) { a[i]=read(); } for(int i=1,x,y;i<n;i++) { x=read(); y=read(); add(x,y); add(y,x); } dfs(1,0); for(int u=1;u<=n;u++){for(int i=1;i<=lg;i++){f[u][i]=f[f[u][i-1]][i-1];}} int ans=0; mx[0]=inf; get_cent(1,0,n); divide(1,0,n); for(register int u=1;u<=n;u++){upd(u,a[u]);} for(register int i=1,opt,x,y;i<=m;i++) { opt=read(); x=read(); y=read(); x^=ans; y^=ans; if(opt==0) { ans=query(x,y); printf("%d\n",ans); } else { upd(x,y-a[x]); a[x]=y; } } } int main() { //freopen("P6329.in","r",stdin); //freopen("P6329.out","w",stdout); work(); return 0; }