Codeforces Round #225 (Div. 1) C. Propagating tree (线段树+dfs序)

这篇博客讨论了一种特殊的树形结构问题,涉及到带点权的树和两种类型的操作。操作1涉及点权的递增与递减,沿着树的路径传播,而操作2为单点查询。解决方法是使用两棵线段树,分别维护奇数层和偶数层节点的修改,从而在查询时快速得到结果。代码中实现了这一算法,并进行了详细注释。
摘要由CSDN通过智能技术生成

传送门

题意:给定一个带点权的树,操作1:顶点u的点权+v,u的孩子节点点权-v,u的孩子节点的孩子节点点权+v...操作2:查询某个点的点权 .

分析:

  • 首先我们可以求出dfs序,但是这里的修改并不是普通的区间修改,而是层数奇偶性相同的顶点加,不同的顶点减 。
  • 我们可以用两棵线段树分别维护奇数层顶点的修改,偶数层顶点的修改。
  • 由于题目只涉及单点查询,所以我们查询的时候分别求出两棵线段树的单点值,然后根据奇偶性加减即可。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pii pair<int,int>
const int maxn = 2e5+10;
const int mx = 40;
const int mod = 1e9+7;
const ll inf = 0x3f3f3f3f3f3f3f3f;
const int INF = 1e9+7;
//给定带点权的树 单点修改 给顶点v点权+x的时候 其子节点点权-x 子节点的子节点点权+x 以此类推直到叶子节点
//单点查询
//由于只涉及单点查询 我们开两个数组d1 d2分别记录修改奇数顶点/偶数顶点的dfs序区间影响
struct  
{
    int to,next;
}e[maxn<<1];
int ecnt,head[maxn];
void add(int v,int u)
{
    e[++ecnt].to=u;
    e[ecnt].next=head[v];
    head[v]=ecnt;
}
int dep[maxn];//dfs序的每个点的深度
int in[maxn],out[maxn],dfsclock;
void dfs(int rt,int par,int depth)//深度
{
    in[rt]=++dfsclock;
    dep[dfsclock]=depth;//注意不是dep[rt]=depth
    for(int i=head[rt];i;i=e[i].next) 
    {
        int to=e[i].to;
        if(to == par) continue;
        dfs(to,rt,depth+1);
    }
    out[rt]=dfsclock;
}
ll d[2][maxn<<2];//两棵线段树
ll tag[2][maxn<<2];
int n,q;
int a[maxn];//初始点权
inline int lc(int &rt) {return rt<<1;}
inline int rc(int &rt) {return rt<<1|1;}
inline void change(int rt,int l,int r,int x,int k)
{
    tag[k][rt]+=x;
    d[k][rt]+=(r-l+1)*x;
}
inline void pushdown(int rt,int l,int r,int k)
{
    int mid=(l+r)>>1;
    if(tag[k][rt])
    {
        change(lc(rt),l,mid,tag[k][rt],k);
        change(rc(rt),mid+1,r,tag[k][rt],k);
    }
    tag[k][rt]=0;
}
inline void updata(int rt,int l,int r,int vl,int vr,int x,int k)
{
    if(r<vl || l>vr) return ;
    if(vl<=l && r<=vr) 
    {
        change(rt,l,r,x,k);
        return ;
    }
    int mid=(l+r)>>1;
    pushdown(rt,l,r,k);
    updata(lc(rt),l,mid,vl,vr,x,k);
    updata(rc(rt),mid+1,r,vl,vr,x,k);
}
inline ll query(int rt,int l,int r,int indx,int k)
{
    if(l == r) return d[k][rt];
    int mid=(l+r)>>1;
    pushdown(rt,l,r,k);
    if(indx<=mid) return query(lc(rt),l,mid,indx,k);
    else return query(rc(rt),mid+1,r,indx,k);
}
int main()
{
    scanf("%d %d",&n,&q);
    for(int i=1;i<=n;i++) scanf("%d",a+i);
    for(int i=1;i<n;i++)
    {
        int v,u;
        scanf("%d%d",&v,&u);
        add(v,u),add(u,v);
    }
    dfs(1,-1,1);
    while(q--)
    {
        int f,x;
        scanf("%d %d",&f,&x);
        if(f == 1)
        {
            int v;
            scanf("%d",&v);
            updata(1,1,n,in[x],out[x],v,dep[in[x]]%2);
        }
        else 
        {
            ll d0=query(1,1,n,in[x],0),
                d1=query(1,1,n,in[x],1);
            if(dep[in[x]]%2 == 0) //偶层顶点 +d0-d1
            {
                printf("%lld\n",a[x]+d0-d1);
            }
            else printf("%lld\n",a[x]+d1-d0);
        }
    }
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值