P6329 【模板】点分树 | 震波

P6329 【模板】点分树 | 震波

来补点分树模板的题解了:

先明确一下点分树的定义:又很多个重心构成的一棵树,且树上的层数关系对应重心的大小

那么我们为什么要建这一颗树呢:因为我们要处理多组询问并且又修改.

然后点分树的建树方式其实在定义中就几乎给出了,就是在求重心时将新老重心连一条边.

然后我们开始思考本题:“所有与 x 距离不超过 k 的城市都将受到影响”:
我们不难想到对于一个重心lca,在它的子树下对答案的贡献(将目标节点设为y)就是:

ySlca[disy,lcakdislca,x]valyySancx[disy,lcakdislca,x]valy

其中,Slca表示在lca的子树内,Sancx类似.
节点ancx表示的是一个既是lca的儿子,又是x的先祖(也可能是本身)的节点.

然后这题的思路就比较明确了:维护两颗动态开点线段树,对于一个点x:
T1维护sumy| ySx disx,y[l,r]
T2维护sumy| ySx disfa,y[l,r]
其中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;
}
posted @   liuboom  阅读(7)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 推荐几款开源且免费的 .NET MAUI 组件库
· 实操Deepseek接入个人知识库
· 易语言 —— 开山篇
· Trae初体验
点击右上角即可分享
微信分享提示