1. 程式人生 > >1076E/Educational Codeforces Round 54-E. Vasya and a Tree<<dfs序 樹狀陣列

1076E/Educational Codeforces Round 54-E. Vasya and a Tree<<dfs序 樹狀陣列

題意

給定一棵樹,初始每個節點權值為零,q次更改,每次修改將以v為頂點的深度為d的子樹全部加上x,最後輸出所有節點的權重。

思路

題目只要求每個點最後的值,那麼經過觀察,發現一個點最後的權值大小隻與他的父節點的更新有關,那麼我們就只需要考慮他的父節點到他這條鏈上的情況,把這條鏈拿出來成為線段,然後維護字尾和就能得到此點上的權值。每個節點的貢獻為給$[h,h+d]$增加$x$,所以維護時,只要在$h+d$點上加上$x$即可。

但是問題考察的是一棵樹,我們就需要動態來完成這條鏈,我們採用dfs序去掃描這棵樹,當一個節點進入時,把他的貢獻算上,退出時減去他的貢獻,這樣就能保證他不會影響別的鏈。

程式碼

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 typedef pair<int,int> PII;
 4 const int maxn=3e5+7;
 5 vector<PII> query[maxn];
 6 vector<int> G[maxn];
 7 long long tree[maxn];
 8 long long ans[maxn];
 9 int depth[maxn],reflect[maxn];
10 int st[maxn],ed[maxn];
11 void dfs(int now,int far) 12 { 13 static int tot=0; 14 st[now]=++tot; 15 reflect[tot]=now; 16 if(far==-1) 17 { 18 depth[now]=1; 19 } 20 for(int i=0;i<G[now].size();i++) 21 { 22 int v=G[now][i]; 23 if(v==far) continue; 24 depth[v]=depth[now]+1
; 25 dfs(v,now); 26 } 27 ed[now]=tot; 28 } 29 void add(int x,int v) 30 { 31 while(x<maxn) 32 { 33 tree[x]+=v; 34 x+=x&-x; 35 } 36 } 37 long long sum(int x) 38 { 39 long long ret=0; 40 while(x>0) 41 { 42 ret+=tree[x]; 43 x-=x&-x; 44 } 45 return ret; 46 } 47 int main() 48 { 49 int n; 50 memset(tree,0,sizeof(tree)); 51 scanf("%d",&n); 52 for(int i=1,u,v;i<=n-1;i++) 53 { 54 scanf("%d%d",&u,&v); 55 G[u].push_back(v); 56 G[v].push_back(u); 57 } 58 dfs(1,-1);//跑出dfs序 59 int q; 60 scanf("%d",&q); 61 while(q--)//儲存所有詢問,將他們按節點分開轉換為dfs序的樹上順序 62 { 63 int v,d,x; 64 scanf("%d%d%d",&v,&d,&x); 65 d+=depth[v]; 66 d=min(d,n); 67 query[st[v]].push_back(make_pair(d,x)); 68 query[ed[v]+1].push_back(make_pair(d,-x));//在退出時清除貢獻 69 } 70 for(int i=1;i<=n;i++) 71 { 72 for(int j=0;j<query[i].size();j++)//把這個節點上更新的都更新了 73 { 74 PII temp=query[i][j]; 75 add(temp.first,temp.second); 76 } 77 ans[reflect[i]]=sum(n)-sum(depth[reflect[i]]-1);//對映返回得到答案 78 } 79 for(int i=1;i<=n;i++) 80 { 81 printf("%lld ",ans[i]); 82 } 83 }