Codeforces 1076E Vasya and a Tree(樹狀陣列)
阿新 • • 發佈:2018-11-13
題意:給你一顆以1為根節點的樹,初始所有節點的權值為0,然後有m個操作,每個操作將點x的所有距離不超過d的節點權值+1,問經過m次操作後每個節點權值是多少?
思路:如果是一個序列,就可以直接用樹狀陣列做,但這是一顆樹,所以我們可以想辦法把它轉化成序列。我們可以先求出每個節點的dfs序,以及深度和子樹的大小,順便記錄每個深度都有哪些節點,子樹的大小用來確認以該節點為根的子樹在dfs序中的範圍,此時便可用樹狀陣列維護了。之後,我們把每個操作按能影響到的深度從大到小排序,即優先處理影響深度大的操作。設當前計算的深度為now,假設所有操的作影響的深度大於now的操作已經計算。如果當前操作影響的深度小於now,說明所有能影響到now深度的操作已經全部操作完了,此時把所有深度為now的節點權值計算出來。每讀取一個操作的資訊,就把操作產生的影響用樹狀陣列維護,因為影響now深度的節點權值已經計算完畢了,所以我把以該操作的操作節點為根的子樹全部加上操作的值 對之前已經計算的答案沒有影響。操作全部完成後,深度從深到淺計算答案即可。
程式碼:
#include<bits/stdc++.h> #define LL long long #define lowbit(x) (x&(-(x))) using namespace std; const int maxn=300010; int deep[maxn],head[maxn],Next[maxn*2],ver[maxn*2],tot,cnt; int sum[maxn],sz[maxn],dfsn[maxn],mx,n; LL c[maxn],ans[maxn]; struct op{ int x,d; LL num; bool operator <(const op& rhs)const{ return (deep[x]+d)>(deep[rhs.x]+rhs.d); } }OP[maxn]; void adde(int x,int y){ ver[++tot]=y; Next[tot]=head[x]; head[x]=tot; } vector<int>re[maxn]; int get_deep(int x,int dep){ deep[x]=dep; dfsn[x]=++cnt; sz[x]=1; mx=max(mx,dep); re[dep].push_back(x); for(int i=head[x];i;i=Next[i]){ int y=ver[i]; if(!deep[y]){ get_deep(y,dep+1); sz[x]+=sz[y]; } } } LL ask(int x){ LL ans=0; for(;x;x-=lowbit(x))ans+=c[x]; return ans; } void add(int x,LL y){ for(;x<=n;x+=lowbit(x))c[x]+=y; } int main(){ int m; scanf("%d",&n); for(int i=1;i<n;i++){ int a,b; scanf("%d%d",&a,&b); adde(a,b); adde(b,a); } get_deep(1,1); scanf("%d",&m); for(int i=1;i<=m;i++){ int a,b; LL c; scanf("%d%d%lld",&a,&b,&c); OP[i]=(op){a,b,c}; } sort(OP+1,OP+1+m); int now=mx; for(int i=1;i<=m;i++){ int tmp=deep[OP[i].x]+OP[i].d; while(now>tmp){ for(int j=0;j<re[now].size();j++){ int x=re[now][j]; ans[x]=ask(dfsn[x]); } now--; } add(dfsn[OP[i].x],OP[i].num); add(dfsn[OP[i].x]+sz[OP[i].x],-OP[i].num); } while(now){ while(now){ for(int j=0;j<re[now].size();j++){ int x=re[now][j]; ans[x]=ask(dfsn[x]); } now--; } } for(int i=1;i<=n;i++) printf("%lld ",ans[i]); }