【Educational Codeforces Round 54 (Rated for Div. 2) E. Vasya and a Tree】 dfs+树状数组

E. Vasya and a Tree

题意

n 0 给你一颗n个点的树,每个点的权值最初为0
m v , d , x 有m个操作,每次操作有三个变量v,d,x
v v < = d + x 操作为在v的距离v<=d的子树内所有节点权值+x
最终统计树上每个点的权值

做法

首先要明确两件事情
1. ( ) 性质1.每个人的操作只会影响到他的子孙(包括自己)
2. ( ) 性质2.每个人只会被他祖先的操作所影响(包括自己)
访 也就是说,如果我们能在访问到某个节点时,统计出所有影响到该节点的祖先操作
就可以统计出这个节点的最终权值
d e p 而对于每个操作,我们只要用一个dep数组保存每个深度被增加的值
线 所有深度大于当前节点的操作都会影响到当前节点,如果用线段树就是一个区间求和问题
为了减少代码量我们用树状数组,更新时只在本次操作的最深的深度更新
1 m a x d e p 这样求一个1-maxdep的前缀和就是所有更新了根节点的操作
1 ( n o w d e p 1 ) 在求一个1-(nowdep-1)的前缀和就是所有不包含当前节点的操作
两个前缀和相减就是当前节点被更新的值
d f s 退 为了保证每个操作只影响自己子树内的节点,在dfs退出子树时
要将当前根节点的所有修改值还原

代码

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
typedef long long ll;
const int maxn = 3e5+10;
int n,m;
ll tree[maxn],ans[maxn];
vector<int> G[maxn],D[maxn],X[maxn];
void add(int x,int val)
{
    while(x<=n)
    {
        tree[x]+=val;
        x=x+(x&-x);
    }
}
ll sum(int x)
{
    ll ans=0;
    while(x)
    {
        ans+=tree[x];
        x=x-(x&-x);
    }
    return ans;
}
void dfs(int x,int fa,int dep)
{
    for(int i=0;i<D[x].size();i++)
    {
        add(min(D[x][i]+dep,n),X[x][i]);//进子树之前更新
    }
    ans[x]=sum(n)-sum(dep-1);//树状数组变区间查询为两个前缀和相减
    //由于性质2,所以在这个地方就可以直接算出当前节点的最终答案
    for(int i=0;i<G[x].size();i++)
    {
        if(G[x][i]==fa) continue;
        dfs(G[x][i],x,dep+1);
    }
    for(int i=0;i<D[x].size();i++)
    {
        add(min(D[x][i]+dep,n),-X[x][i]);//出子树之后还原
    }
}
int main()
{
    int x,y,z;
    scanf("%d",&n);
    for(int i=1;i<=n-1;i++)
    {
        scanf("%d%d",&x,&y);
        G[x].push_back(y);
        G[y].push_back(x);
    }
    scanf("%d",&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&x,&y,&z);
        D[x].push_back(y);
        X[x].push_back(z);
    }
    dfs(1,0,1);
    for(int i=1;i<=n;i++)
    {
        printf("%lld ",ans[i]);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_38891827/article/details/84064341