1. 程式人生 > >【bzoj4765】普通計算姬(雙重分塊)

【bzoj4765】普通計算姬(雙重分塊)

efi ref space include pos gif signed problem 。。

  題目傳送門:http://www.lydsy.com/JudgeOnline/problem.php?id=4765

  這道題已經攢了半年多了。。。因為懶,一直沒去寫。。。所以今天才把這道題寫出來。。。

  如果是要維護區間權值和、子樹權值和,都可以用線段樹/樹狀數組輕松解決。但是這道題要維護的是子樹權值和的區間和,這就比較難搞了。

  當需要維護一些看起來很難直接維護的信息時,我們一般會想到分塊。於是考慮這樣的分塊:按編號把每√n個節點劃分為一塊,維護每一塊所有節點的sum值的和,然後再維護每個節點的sum值。單節點的sum可以用樹狀數組/線段樹維護,但為了降低時間復雜度,我們可以用分塊維護dfs序的區間和的前綴和,這樣的單節點修改復雜度為O(√n),單節點查詢復雜度為O(1)。

  時間復雜度:修改操作O(√n),查詢操作O(√n),總時間復雜度O((n+m)√n)。

  具體實現細節:維護第一層分塊(即sum值的和)時可以在dfs遍歷樹時一個數組記錄每個節點修改時對每個塊的貢獻,然後修改時直接統計貢獻修改塊的值就行了;第二層分塊(即單節點的sum)時可以分別維護塊的前綴和與每個節點在所在塊內的前綴和,查詢時把兩部分加起來就行了。

  另外,答案要開unsigned long long!

  代碼:

技術分享圖片
#include<cstdio>
#include<cstring>
#include
<cmath> #include<cstdlib> #include<ctime> #include<algorithm> #include<queue> #include<vector> #define ll long long #define ull unsigned long long #define max(a,b) (a>b?a:b) #define min(a,b) (a<b?a:b) #define inf 0x3f3f3f3f #define mod 1000000007 #define eps 1e-18 inline ll read() { ll tmp
=0; char c=getchar(),f=1; for(;c<0||9<c;c=getchar())if(c==-)f=-1; for(;0<=c&&c<=9;c=getchar())tmp=(tmp<<3)+(tmp<<1)+c-0; return tmp*f; } using namespace std; struct edge{ int to,nxt; }e[200010]; int fir[100010],l[100010],r[100010],pos[100010]; ull sum1[350],sum2[100010]; int a[100010],tmp[100010]; ull sum[350]; int w[100010][350]; int n,m,size,tot=0,root; void addedge(int x,int y){e[tot].to=y; e[tot].nxt=fir[x]; fir[x]=tot++;} void dfs(int now,int fa) { if(now!=root){ for(int i=0;i*size<n;i++)w[now][i]=w[fa][i]; } ++w[now][now/size]; l[now]=tot; pos[now]=tot++; for(int i=fir[now];~i;i=e[i].nxt) if(e[i].to!=fa)dfs(e[i].to,now); r[now]=tot-1; } void add(int x,int k) { int i,id=pos[x]/size; a[x]+=k; for(i=id;i*size<n;i++)sum1[i]+=k; for(i=pos[x];i<(id+1)*size&&i<n;i++)sum2[i]+=k; for(i=0;i*size<n;i++)sum[i]+=1ll*w[x][i]*k; } ull getsum(int x) { if(x<0)return 0; else return sum2[x]+(x<size?0:sum1[x/size-1]); } ull query(int L,int R) { int i,idL=L/size,idR=R/size; ull ans=0; if(idL==idR){ for(i=L;i<=R;i++)ans+=getsum(r[i])-getsum(l[i]-1); } else{ for(i=idL+1;i<idR;i++)ans+=sum[i]; for(i=L;i<(idL+1)*size&&i<n;i++)ans+=getsum(r[i])-getsum(l[i]-1); for(i=idR*size;i<=R;i++)ans+=getsum(r[i])-getsum(l[i]-1); } return ans; } int main() { int i; n=read(); m=read(); size=(int)sqrt(n); for(i=0;i<n;i++)tmp[i]=read(); for(i=0;i<n;i++)fir[i]=-1; for(i=1;i<=n;i++){ int x=read(),y=read(); if(!x)root=y-1; else addedge(x-1,y-1),addedge(y-1,x-1); } tot=0; dfs(root,-1); for(i=0;i<n;i++)add(i,tmp[i]); for(i=1;i<=m;i++){ int op=read(),x=read(),y=read(); if(op==1)add(x-1,y-a[x-1]); else printf("%llu\n",query(x-1,y-1)); } return 0; }
又臭又長

【bzoj4765】普通計算姬(雙重分塊)