点分树/动态点分治
引入
点分治能够解决单次询问,但无法处理多次询问和修改,这是我们就需要用点分树来解决
简介
多次询问时我们并不需要每次去求重心,同样的,修改一个点也不需要整个树重新遍历一次
我们将每次分治的重心连边构成一棵虚树,那么这棵树上点到根节点的路径就是处理这个点的分治顺序
因此对于询问和修改我们都只需要遍历这条链即可,又由于分治的性质,链的长度一定是 \(O(\log n)\) 的
实现
我们先考虑单次询问:就是点分治的板题,我们定义 \(cnt_{u,i}\) 为以 \(u\) 为根的子树中距离小于等于 \(i\) 的权值和,\(rt(u)\) 为以 \(u\) 为根的子树的重心,那么对于每个重心 \(u\),它的贡献是 \(cnt_{u,k-dis(x,u)}-cnt_{rt(son(u)),k-dis(x,u)}\),换到点分树上就是 \(cnt_{u,k-dis(x,u)}-cnt_{son(u),k-dis(x,u)}\),我们直接在点分树上用动态开点线段树,一个点开两棵,用来维护 \(cnt_{u,dis(x,u)},cnt_{u,dis(fa(u),x)}\),对于每个询问和修改,对应更新链上的点即可,复杂度 \(O(n\log^2 n)\)
#include<bits/stdc++.h>
using namespace std;
#define mid ((l+r)>>1)
#define lson ls[p],l,mid
#define rson rs[p],mid+1,r
const int N=1e5+5;
vector<int> e[N];
int dep[N],siz[N],mx[N],ff[N],vis[N],n,q,a[N],rt,son[N],top[N],dfn[N],f[N],tim,lst;
struct segtree{
int tr[N<<6],ls[N<<6],rs[N<<6],tot,rt[N];
void pushup(int p){tr[p]=tr[ls[p]]+tr[rs[p]];}
void update(int& p,int l,int r,int x,int v){
if(!p) p=++tot;
if(l==r){tr[p]+=v;return;}
if(x<=mid) update(lson,x,v);
else update(rson,x,v);
pushup(p);
}
int query(int p,int l,int r,int L,int R){
if(L>R) return 0;
if(L<=l&&r<=R) return tr[p];
int res=0;
if(L<=mid) res+=query(lson,L,R);
if(R>mid) res+=query(rson,L,R);
return res;
}
}t1,t2;
void dfs(int u,int fa){
dep[u]=dep[fa]+1,f[u]=fa,siz[u]=1;
for(auto v:e[u]){
if(v==fa) continue;
dfs(v,u);siz[u]+=siz[v];
if(siz[v]>siz[son[u]]) son[u]=v;
}
}
void dfs2(int u,int t){
top[u]=t,dfn[u]=++tim;
if(son[u]) dfs2(son[u],t);
for(auto v:e[u]){
if(v==son[u]||v==f[u]) continue;
dfs2(v,v);
}
}
void get_rt(int u,int fa,int sum,int& rt){
siz[u]=1,mx[u]=0;
for(auto v:e[u]){
if(v==fa||vis[v]) continue;
get_rt(v,u,sum,rt);
siz[u]+=siz[v],mx[u]=max(mx[u],siz[v]);
}
mx[u]=max(mx[u],sum-siz[u]);
if(mx[u]<mx[rt]) rt=u;
}
void rebuild(int u,int sum){
vis[u]=1;
for(auto v:e[u]){
if(vis[v]) continue;
int sz=siz[v]<siz[u]?siz[v]:sum-siz[u];rt=0;
get_rt(v,u,sz,rt);
ff[rt]=u;rebuild(rt,sz);
}
}
int lca(int x,int y){
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]]) swap(x,y);
x=f[top[x]];
}
return dep[x]>dep[y]?y:x;
}
int dis(int x,int y){return dep[x]+dep[y]-2*dep[lca(x,y)];}
void update(int x,int v){
for(int i=x;i;i=ff[i]){
t1.update(t1.rt[i],0,n-1,dis(i,x),v);
if(ff[i]) t2.update(t2.rt[i],0,n-1,dis(ff[i],x),v);
}
}
int query(int x,int k){
int res=0,pre=0;
for(int i=x;i;pre=i,i=ff[i]){
res+=t1.query(t1.rt[i],0,n-1,0,k-dis(x,i));
if(pre) res-=t2.query(t2.rt[pre],0,n-1,0,k-dis(x,i));
}
return res;
}
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n>>q;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<n;i++){
int x,y;cin>>x>>y;
e[x].push_back(y),e[y].push_back(x);
}
mx[0]=1e9,rt=0;
dfs(1,0);dfs2(1,1);
get_rt(1,0,n,rt);rebuild(rt,n);
for(int i=1;i<=n;i++) update(i,a[i]);
while(q--){
int op,x,k;cin>>op>>x>>k;
x^=lst,k^=lst;
if(op) update(x,k-a[x]),a[x]=k;
else cout<<(lst=query(x,k))<<"\n";
}
return 0;
}

浙公网安备 33010602011771号