P6329 【模板】点分树 | 震波

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在点分树上的父亲

然后对于每个操作,在点分树上不断向上跳并执行对应操作就好了

然后这题就愉快的做完了

我是绝对不会告诉你我卡了一下午常的 😦(((

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 @ 2024-12-06 12:03  liuboom  阅读(36)  评论(0)    收藏  举报