Gym 100589A Queries on the Tree (樹狀陣列+分塊均攤思想)
阿新 • • 發佈:2018-12-09
#include<bits/stdc++.h> using namespace std; #define debug puts("YES"); #define rep(x,y,z) for(int (x)=(y);(x)<(z);(x)++) #define read(x,y) scanf("%d%d",&x,&y) #define ll long long #define lrt int l,int r,int rt #define lson l,mid,rt<<1 #define rson mid+1,r,rt<<1|1 /* 題目大意:一棵樹兩種操作, 查詢子樹的權重和,修改同一層節點的權重。 同一層的節點的話就用vector來分層,記住要記錄 DFS區間序的左端點即可。 首先思考下,同一層的修改操作, 有兩種,一種是在層權重陣列中直接修改,O(1), 然後查詢時遍歷所有層,通過二分來統計節點累加權重。O(nlogn) 一種是樹狀陣列通過DFS序左端點修改,O(nlogn) 查詢時可以直接樹狀陣列查詢區間和O(logn)。 如果我們採用其中的任何一種,為什麼會超時呢? 因為存在某一層節點數量太多的情況,直接使複雜度弱化為O(nmlogn)。 這樣就要引入資料結構中常用的分塊的思想(不是數論中的分塊。。。) 個人感覺其實就是均攤,當深度節點數量大於X時,採用第二種修改, 否則採用第一種,至於查詢,N/x*logn級別的。因為這樣需要統計權重的層數 不超過N/x。 那麼我們把函式式子列出來發現x取根號N答案最優,根號N的界限就是這樣來的。 有了思路下面就是秀操作的時候了。 另外一個小優化是要事先把大於界限的層下標抽取出來。 */ const int maxn =1e5+5; ///資料範圍區域 int n,m,ub; int op,x,y; ///深度節點集合和權重陣列 vector<int> g[maxn],big; ll w[maxn];///資料範圍要注意 int maxd; ///鏈式前向星 struct node { int u,nxt; }edge[maxn<<1]; int head[maxn],tot=0,ti; void init() { big.clear(); for(int i=0;i<=n;i++) g[i].clear(); memset(w,0,sizeof(w)); memset(head,-1,sizeof(head)); tot=ti=maxd=0; } void add(int x,int y) { edge[tot]=node{y,head[x]}; head[x]=tot++; } ///DFS序區間建立 int pl[maxn],pr[maxn]; void dfs(int u,int pre,int h) { maxd=max(maxd,h); pl[u]=++ti; g[h].push_back(ti); for(int i=head[u];~i;i=edge[i].nxt) { int v=edge[i].u; if(v==pre) continue; dfs(v,u,h+1); } pr[u]=ti; } ///bit ll tree[maxn<<1]; int lowbit(int x) { return x&(-x); } void refresh(int x,int d) { for(;x<maxn;tree[x]+=d,x+=lowbit(x)); } ll sum(int x) { ll ret=0; for(;x>0;ret+=1LL*tree[x],x-=lowbit(x)); return ret; } int main() { init(); scanf("%d%d",&n,&m); ub=sqrt(n); for(int i=1;i<n;i++) { scanf("%d%d",&x,&y); add(x,y),add(y,x); } dfs(1,-1,0);///建立DFS序 for(int i=0;i<=maxd;i++) { if(g[i].size()<=ub) continue; sort(g[i].begin(),g[i].end()); big.push_back(i); } for(int i=0;i<m;i++) { scanf("%d",&op); if(op==1) { scanf("%d%d",&x,&y); int sz=g[x].size(); if(sz<=ub)///樹狀陣列暴力修改 { for(int j=0;j<sz;j++) refresh(g[x][j],y); } else { w[x]+=y; } } else { scanf("%d",&x); int l=pl[x],r=pr[x],sz=big.size(); ll ans=1LL*(sum(r)-sum(l-1));///log查詢 for(int j=0;j<sz;j++) { int d=big[j]; int tx=upper_bound(g[d].begin(),g[d].end(),pr[x])-g[d].begin(); int ty=lower_bound(g[d].begin(),g[d].end(),pl[x])-g[d].begin(); ans+=1LL*(tx-ty)*w[d]; } printf("%lld\n",ans); } } return 0; }