P6329 【模板】点分树 | 震波

【题意】

给一个树,要求你支持如下操作

1.修改某个点的点权 2.询问到某个点的距离小于等于k的点权和

【分析】

首先看到这种树上距离相关的操作不难想到需要用点分治,由于其需要支持修改操作,所以我们要用到动态点分治并配合数据结构来维护

具体地,我们先建立点分树,对于树上的每个节点,维护2个树状数组,分别记录到子树内到当前距离为i的点权和、子树内到fa的距离为i的点权和

每次修改维护以上两个值,在查询的时候,我们先计算到询问点u距离为k的点权和,然后再向上计算到fa距离为k-dis(u,fa)的点权和,减去重复的部分,如此直到算到根节点结束即可

注意一个小细节,树状数组不能全开满,要根据子树内节点个数动态开点

【代码】

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
int n,m;
int head[maxn],tot,v[maxn];
struct edge
{
    int to,nxt;
}e[maxn<<1];
void add(int x,int y)
{
    e[++tot].to=y; e[tot].nxt=head[x]; head[x]=tot;
}
int vis[maxn],dfsfa[maxn],size,gsiz,siz[maxn],root;
int dep[maxn],st[maxn][20],val[maxn];
void dfs(int u,int fa)
{
    dep[u]=dep[fa]+1; st[u][0]=fa;
    for(int i=1;i<=18;i++) st[u][i]=st[st[u][i-1]][i-1];
    for(int i=head[u];i;i=e[i].nxt)
    {
        int to=e[i].to;
        if(to==fa) continue;
        dfs(to,u);
    }
}
int cnt,p[maxn];
void findrt(int u,int fa)
{
    siz[u]=1; p[++cnt]=u;
    int maxsiz=0;
    for(int i=head[u];i;i=e[i].nxt)
    {
        int to=e[i].to;
        if(to==fa || vis[to]) continue;
        findrt(to,u);
        siz[u]+=siz[to];
        maxsiz=max(maxsiz,siz[to]);
    }
    maxsiz=max(maxsiz,size-siz[u]);
    if(maxsiz<gsiz)
    {
        gsiz=maxsiz;
        root=u;
    }
}
int lowbit(int x)
{
    return x&(-x);
}
struct seg
{
    vector <int> c;
    void build(int len)
    {
        for(int i=0;i<=len+2;i++) c.push_back(0);
    }
    void add(int x,int y)
    {
        for(int i=x;i<c.size();i+=lowbit(i))
            c[i]+=y;
    }
    int query(int x)
    {
        int res=0;
        x=min(x,(int)c.size()-1);
        for(int i=x;i;i-=lowbit(i))
            res+=c[i];
        return res;
    }
}S1[maxn],S2[maxn];
int lca(int x,int y)
{
    if(dep[x]<dep[y]) swap(x,y);
    for(int i=18;i>=0;i--)
        if(dep[st[x][i]]>=dep[y])
            x=st[x][i];
    if(x==y) return x;
    for(int i=18;i>=0;i--)
        if(st[x][i]!=st[y][i])
            x=st[x][i],y=st[y][i];
    return st[x][0];
}
int getdis(int x,int y)
{
    return dep[x]+dep[y]-2*dep[lca(x,y)];
}
void getpath(int rt,int u,int fa,int len)
{
    S1[rt].add(len,val[u]);
    for(int i=head[u];i;i=e[i].nxt)
    {
        int to=e[i].to;
        if(to==fa || vis[to]) continue;
        getpath(rt,to,u,len+1);
    }
}
void solve(int u,int fa)
{
    vis[u]=1; dfsfa[u]=fa;
    S2[u].build(cnt);
    if(fa)
        for(int i=1;i<=cnt;i++)
            S2[u].add(getdis(p[i],fa),val[p[i]]);
    S1[u].build(cnt);
    for(int i=head[u];i;i=e[i].nxt)
    {
        int to=e[i].to;
        if(vis[to]) continue;
        getpath(u,to,u,1);
    }
    for(int i=head[u];i;i=e[i].nxt)
    {
        int to=e[i].to;
        if(vis[to]) continue;
        gsiz=size=siz[to]; root=0;
        cnt=0;
        findrt(to,0);
        solve(root,u);
    }
}
int query(int x,int k)
{
    int res=0,u=x;
    res+=S1[x].query(k)+val[x];
    while(dfsfa[u])
    {
        int d=getdis(x,dfsfa[u]);
        if(d<=k) res+=val[dfsfa[u]]+S1[dfsfa[u]].query(k-d)-S2[u].query(k-d);
        u=dfsfa[u];
    }
    return res;
}
void update(int x,int y)
{
    int u=x;
    while(dfsfa[u])
    {
        int d=getdis(x,dfsfa[u]);
        S2[u].add(d,-val[x]);
        S2[u].add(d,y);
        u=dfsfa[u];
        S1[u].add(d,-val[x]);
        S1[u].add(d,y);
    }
    val[x]=y;
}
int main()
{

    scanf("%d%d",&n,&m);
    int x,y;     
    for(int i=1;i<=n;i++) scanf("%d",&val[i]);
    for(int i=1;i<n;i++)
    {
        scanf("%d%d",&x,&y);
        add(x,y); add(y,x);
    }
    dfs(1,0);
    size=gsiz=n; cnt=0;
    findrt(1,0);
    solve(root,0);
    int ans=0;
    for(int i=1;i<=m;i++)
    {
        int op;
        scanf("%d%d%d",&op,&x,&y);
        x^=ans; y^=ans;
        if(!op) printf("%d\n",ans=query(x,y));
        else update(x,y);
    }
    return 0;
}

 

posted @ 2021-05-20 21:55  andyc_03  阅读(141)  评论(0)    收藏  举报