1. 程式人生 > >CF E. Vasya and a Tree】 dfs+樹狀陣列(給你一棵n個節點的樹,每個點有一個權值,初始全為0,m次操作,每次三個數(v, d, x)表示只考慮以v為根的子樹,將所有與v點距離小於等於d的點權值全部加上x,求所有操作完畢後,所有節點的值)

CF E. Vasya and a Tree】 dfs+樹狀陣列(給你一棵n個節點的樹,每個點有一個權值,初始全為0,m次操作,每次三個數(v, d, x)表示只考慮以v為根的子樹,將所有與v點距離小於等於d的點權值全部加上x,求所有操作完畢後,所有節點的值)

題意:

給你一棵n個節點的樹,每個點有一個權值,初始全為0,m次操作,每次三個數(v, d, x)表示只考慮以v為根的子樹,將所有與v點距離小於等於d的點權值全部加上x,求所有操作完畢後,所有節點的值

 

首先要明確兩件事情
性質1.每個人的操作只會影響到他的子孫(包括自己) 性質1.每個人的操作只會影響到他的子孫(包括自己)性質1.每個人的操作只會影響到他的子孫(包括自己)
性質2.每個人只會被他祖先的操作所影響(包括自己) 性質2.每個人只會被他祖先的操作所影響(包括自己)性質2.每個人只會被他祖先的操作所影響(包括自己)
也就是說,如果我們能在訪問到某個節點時,統計出所有影響到該節點的祖先操作 也就是說,如果我們能在訪問到某個節點時,統計出所有影響到該節點的祖先操作也就是說,如果我們能在訪問到某個節點時,統計出所有影響到該節點的祖先操作
就可以統計出這個節點的最終權值 就可以統計出這個節點的最終權值就可以統計出這個節點的最終權值
而對於每個操作,我們只要用一個dep陣列儲存每個深度被增加的值 而對於每個操作,我們只要用一個dep陣列儲存每個深度被增加的值而對於每個操作,我們只要用一個dep陣列儲存每個深度被增加的值
所有深度大於當前節點的操作都會影響到當前節點,如果用線段樹就是一個區間求和問題 所有深度大於當前節點的操作都會影響到當前節點,如果用線段樹就是一個區間求和問題所有深度大於當前節點的操作都會影響到當前節點,如果用線段樹就是一個區間求和問題
為了減少程式碼量我們用樹狀陣列,更新時只在本次操作的最深的深度更新 為了減少程式碼量我們用樹狀陣列,更新時只在本次操作的最深的深度更新為了減少程式碼量我們用樹狀陣列,更新時只在本次操作的最深的深度更新
這樣求一個1−maxdep的字首和就是所有更新了根節點的操作 這樣求一個1-maxdep的字首和就是所有更新了根節點的操作這樣求一個1−maxdep的字首和就是所有更新了根節點的操作
在求一個1−(nowdep−1)的字首和就是所有不包含當前節點的操作 在求一個1-(nowdep-1)的字首和就是所有不包含當前節點的操作在求一個1−(nowdep−1)的字首和就是所有不包含當前節點的操作
兩個字首和相減就是當前節點被更新的值 兩個字首和相減就是當前節點被更新的值兩個字首和相減就是當前節點被更新的值
為了保證每個操作隻影響自己子樹內的節點,在dfs退出子樹時 為了保證每個操作隻影響自己子樹內的節點,在dfs退出子樹時為了保證每個操作隻影響自己子樹內的節點,在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; }
View Code