1. 程式人生 > >CF Div2 E. Vasya and a Tree(思維 + 線段樹)

CF Div2 E. Vasya and a Tree(思維 + 線段樹)

題目連結:

E. Vasya and a Tree

 

題意:

給定一個以 1 為根節點的樹,初始每個節點的權值為 0 。有 m 次操作,每次把以 vi 為祖先且離 vi 的距離小於 di 的所有節點(包括 vi 本身)的權值加上 xi 。問所有操作結束後,每個節點的權值。

 

思路:

一個點只會影響它的子孫節點(包括自己),且只會被它的祖先節點影響(包括自己)。

 

所以,我們 dfs 到一個節點 v 時,我們把以它為操作物件的操作完成,那麼相當於對於該節點的所有操作都已被完成,那麼我們可以直接算出這個節點的最終權值。當 dfs 回溯到該節點時,我們把之前做的操作刪除即可,因為關於該節點的操作不能影響不以該節點為祖先的節點。這樣一次 dfs 就能算出所有節點的最終答案。

 

那麼,操作怎樣進行呢?我們知道:到當前為止的所有操作都會對該節點的子孫產生影響(如果在範圍內的話),所以比如說操作為v,d,x,當前節點深度為dep,那麼所有深度在 [dep,dep+d] 範圍內的節點都要加 x 。因此我們可以用線段樹來維護區間和。當前節點的最終權值就是用線段樹查詢區間 [dep,dep] 的權值和。

 

Code:

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;

const int MAX = 3e5+10;
const ll mod = 1e9+7;

typedef struct{
    int d;
    ll x;
}Point;

int n,m;
vector<int>mp[MAX];
vector<Point>op[MAX];
ll sum[MAX<<2],Add[MAX<<2];
ll res[MAX];

/*線段樹*/

void Pushup(int root)
{
    sum[root]=sum[root<<1]+sum[root<<1|1];
}

void Build(int l,int r,int root)
{
    if(l==r){
        sum[root]=0;
        return;
    }
    int mid = (l+r)>>1;
    Build(l,mid,root<<1);
    Build(mid+1,r,root<<1|1);
    Pushup(root);
}

void Pushdown(int root,int ln,int rn)
{
    if(Add[root]){
        Add[root<<1]+=Add[root];
        Add[root<<1|1]+=Add[root];
        sum[root<<1]+=Add[root]*ln;
        sum[root<<1|1]+=Add[root]*rn;
        Add[root]=0;
    }
}

void Update(int L,int R,ll c,int l,int r,int root)
{
    if(L<=l&&r<=R){
        sum[root]+=c*(r-l+1);
        Add[root]+=c;
        return;
    }
    int mid = (l+r)>>1;
    Pushdown(root,mid-l+1,r-mid);
    if(L<=mid)  Update(L,R,c,l,mid,root<<1);
    if(R>mid)   Update(L,R,c,mid+1,r,root<<1|1);
    Pushup(root);
}

ll Query(int L,int R,int l,int r,int root)
{
    if(L<=l&&r<=R){
        return sum[root];
    }
    int mid = (l+r)>>1;
    Pushdown(root,mid-l+1,r-mid);
    ll ans=0;
    if(L<=mid)  ans+=Query(L,R,l,mid,root<<1);
    if(R>mid)   ans+=Query(L,R,mid+1,r,root<<1|1);
    return ans;
}

/*線段樹*/

void dfs(int root,int fa,int dep)
{
    //新增操作
    for(int i=0;i<op[root].size();i++){
        Point now = op[root][i];
        Update(dep,min(dep+now.d,n),now.x,1,n,1);
    }
    res[root]=Query(dep,dep,1,n,1);
    for(int i=0;i<mp[root].size();i++){
        int v = mp[root][i];
        if(v==fa)   continue;
        dfs(v,root,dep+1);
    }
    //刪除操作
    for(int i=0;i<op[root].size();i++){
        Point now = op[root][i];
        Update(dep,min(dep+now.d,n),-1*now.x,1,n,1);
    }
}

int main()
{
    scanf("%d",&n);
    for(int i=0;i<n-1;i++){
        int x,y;
        scanf("%d%d",&x,&y);
        mp[x].push_back(y);
        mp[y].push_back(x);
    }
    Build(1,n,1);
    scanf("%d",&m);
    while(m--){
        int v,d;
        ll x;
        scanf("%d%d%lld",&v,&d,&x);
        op[v].push_back(Point{d,x});
    }
    dfs(1,-1,1);
    printf("%lld",res[1]);
    for(int i=2;i<=n;i++){
        printf(" %lld",res[i]);
    }
    printf("\n");
    return 0;
}