Codeforces383 C. Propagating tree (奇偶性分层+线段树)

题意:

给一颗n个节点的树,每个节点有点权,编号为1的节点是树的根
有m次操作:
操作1是给x加上val,然后给x的所有子节点加上-val,x的子节点的子节点加上-(-val),不断传递
操作2是查询点x的点权

解法:

显然题目是子树问题,先dfs序处理成序列,然后考虑用线段树做

难点主要在于如何处理每次向下传递的时候,添加的数值要取相反数。

设d(x)为x到根的距离的奇偶性(0或者1)
修改x的时候,以x为根的子树中,所有与x深度奇偶性相同的点,都是+val
而所有与x深度奇偶性不同的点,都是-val

那么可以将奇数层和偶数层的点分开,按奇偶性建立两颗线段树
设x的子树范围为[L,R],
在修改x的时候,与x奇偶性相同的树[L,R]添加val,这样做是为了将[L,R]中与x奇偶性相同的点全部加上val
并且与x奇偶性不同的树[L,R]减少val,这样做是为了将[L,R]中与x奇偶性不同的点全部加上val

奇数树中,深度为偶数的位置是没用的,直接忽略,偶数树同理

总结:这题按奇偶性建树避免了对每层+val和-val的讨论
似乎可以看作是一种处理分层子树问题的套路

code:
#include<bits/stdc++.h>
using namespace std;
const int maxm=2e5+5;
vector<int>g[maxm];
int L[maxm],R[maxm];
int rk[maxm],idx;
int d[maxm];
int v[maxm];
int n,m;
struct Node{
    int a[maxm<<2];
    void pushdown(int node){
        if(!a[node])return ;
        a[node*2]+=a[node];
        a[node*2+1]+=a[node];
        a[node]=0;
    }
    void update(int st,int ed,int val,int l,int r,int node){
        if(st<=l&&ed>=r){
            a[node]+=val;
            return ;
        }
        pushdown(node);
        int mid=(l+r)/2;
        if(st<=mid)update(st,ed,val,l,mid,node*2);
        if(ed>mid)update(st,ed,val,mid+1,r,node*2+1);
    }
    int ask(int x,int l,int r,int node){
        if(l==r)return a[node];
        pushdown(node);
        int mid=(l+r)/2;
        if(x<=mid)return ask(x,l,mid,node*2);
        else return ask(x,mid+1,r,node*2+1);
    }
}T[2];
void dfs(int x,int fa,int dep){
    d[x]=dep;
    L[x]=++idx;
    rk[idx]=x;
    for(int v:g[x]){
        if(v==fa)continue;
        dfs(v,x,dep^1);
    }
    R[x]=idx;
}
signed main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)scanf("%d",&v[i]);
    for(int i=1;i<n;i++){
        int a,b;scanf("%d%d",&a,&b);
        g[a].push_back(b);
        g[b].push_back(a);
    }
    dfs(1,-1,0);
    while(m--){
        int op;scanf("%d",&op);
        if(op==1){//updata
            int x,val;scanf("%d%d",&x,&val);
            T[d[x]].update(L[x],R[x],val,1,n,1);//x子树中和x一样深度的都要加
            T[d[x]^1].update(L[x],R[x],-val,1,n,1);//x子树中和x不一样深度的都要减
        }else{//ask
            int x;scanf("%d",&x);
            int ans=v[x]+T[d[x]].ask(L[x],1,n,1);//因为v[x]开始没有加到线段树里面,所以要加上
            printf("%d\n",ans);
        }
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值