ACM-ICPC 2018 瀋陽賽區網路預賽 J. Ka Chang (分塊+dfs序+樹狀陣列)
阿新 • • 發佈:2018-12-09
題意:
給你一顆n個節點的樹,每一個節點的值為0。
q種操作,1.將深度為L的點的權值加x,根節點的深度為0
2.輸出以x為根節點的子樹的權值和
解析:
這道題最後想到了分塊,因為我想到用的一種方法需要用[1e5][1e5]的空間去維護,有空間問題,
然後藉此想到分塊可以處理,小的部分暴力,大的部分用陣列儲存狀態,這樣就存的下了
不過我想的是按層來分塊,前block層,和後block層,最後發現並沒有什麼用
搜了題解,是按照每一層內的點的個數來分類的,因為最暴力的做法,就是每一次修改,
把該層每一個點都更新一遍,所以我們就將每一層按照頂點數分塊,
對於點>block的層稱為重層,點<block的層稱為輕層。
題解的這個分塊方法主要不是解決空間的問題,而是通過block來解決時間的問題
對於輕層,因為點的個數<block,所以我們抓住這個,更新的時候用暴力更新,每一個點
對應樹狀陣列更新一遍,時間O(block*logn),然後查詢的時候,就用樹狀陣列查詢O(logn)
對於重層,重層的層的數量是n/block,我們抓住這個,更新的時候,就將更新的值儲存在數組裡O(1),
查詢的時候再遍歷每一層查詢O(n/block*logn)
最後就是一些細節了,對於樹狀陣列插入的時候,我們是按照每一個點的dfs序下標插入的,這樣
我們查詢x的子樹時,我們只要知道x的dfs序和x的子樹大小,就可以在樹狀陣列中查到該子樹內輕層
的點的權值和
然後對於重層,更新的時候只要存每一個重層更新的權值和,然後查詢x的子樹的時候,從x層遍歷
在它下面的重層,對於每一個重層,依照dfs序進行兩次二分,查出該層屬於x的子數的點的個數inc
再*mark,就是答案了
#include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <set> #include <vector> #include <map> using namespace std; typedef long long ll; const int MAXN = 1e5+10; vector<int> edge[MAXN]; vector<int> dep[MAXN]; map<int,int> mdfs; int sz[MAXN]; int md[MAXN]; ll c[MAXN]; ll mark[MAXN]; int n; int maxdep,big[MAXN],cnt; int lowbit(int x) { return x&(-x); } void add(int x,int y) { int i; for(i=x;i<=n;i+=lowbit(i)) c[i]+=y; } ll sum(int x) { int i; ll s=0; for(i=x;i>0;i-=lowbit(i)) { s+=c[i]; } return s; } int dfs(int x,int d,int &ind) { maxdep=max(maxdep,d); mdfs[x]=ind; dep[d].push_back(ind); md[x]=d; sz[x]=1; for(int i=0;i<edge[x].size();i++) { ind++; sz[x]+=dfs(edge[x][i],d+1,ind); } return sz[x]; } int main() { int q; scanf("%d%d",&n,&q); int block=sqrt(n); int x,y; maxdep=0; for(int i=1;i<n;i++) { scanf("%d%d",&x,&y); edge[x].push_back(y); } int ind=1; dfs(1,1,ind); cnt=0; for(int i=1;i<=maxdep;i++) { if(dep[i].size()>block) big[cnt++]=i; } int id; for(int i=0;i<q;i++) { scanf("%d",&y); if(y==1) { scanf("%d%d",&x,&id); x++; if(dep[x].size()<=block) { for(int j=0;j<dep[x].size();j++) add(dep[x][j],id); } else { mark[x]+=id; } } else { scanf("%d",&x); int nex=mdfs[x]+sz[x]; ll res=sum(nex-1)-sum(mdfs[x]-1); int beg=lower_bound(big,big+cnt,md[x])-big; for(int j=beg;j<cnt;j++) { int h=big[j]; int inc=upper_bound(dep[h].begin(),dep[h].end(),nex-1)-(lower_bound(dep[h].begin(),dep[h].end(),mdfs[x])); res+=(inc)*mark[h]; } printf("%lld\n",res); } } return 0; }