題意:給一棵樹,只有點權無邊權, ,每次詢問求以一個點為中心,半徑為k的全職和。
考慮動態樹分治。我們對於每個點(點分樹)維護兩個樹狀數組。兩個樹狀數組都以距離為下標,權值為內容。第一個樹狀數組維護子樹中距離該點為k的權值和,第二個維護距離該點父親距離為k的權值和。這樣改權值時我們暴力爬樹高,loglog復雜度(log的樹高加上log的樹狀數組)。查詢的時候一樣爬樹高,要注意容斥(把當前子樹k的先加起來,往祖先上爬,如果距離小於k,假設為d,我們到祖先上去求一個k-d,再容斥掉原來這棵子樹里被計算過的,這就是第二個樹狀數組的用處)。
時間復雜度0(nloglog),空間復雜度O(nlog)(如果用線段樹還要加一個log。這里BB一句,為什么樹狀數組不會爆呢,因為你每一層開的大小為子樹大小的話,每層總和n,總共log層,空間就是nlog的。其實就跟點分治的時間復雜度證明一樣。用vector來開並且加上函數resize()就可以辦到了。)
這道題花了整整一天才寫出來。。。一直RE(實際是WA,畢竟防離線加密,如果答案錯了后面輸入都是錯的),總結一下錯誤:
1.想的只用一個樹狀數組維護,實際上為了容斥必須要用上第二個樹狀數組。
2.更新時,一開始要把自己丟在自己的第二個樹狀數組里面
3.2這玩意肯定要寫在爬樹循環外面啊(你是SB嗎)
4.應該用qsum而不是query去爬樹高(一開始腦袋抽了。。)
5.往樹上爬的時候是不會中途退出的,不會因為有一個祖先爬不上去就終止,說不定有一個爺爺就在你旁邊你可以過去呢。
對於點分樹的題就想象成爬山吧,我們用每一層的重心將點們分割開來,每次爬樹高都是解鎖區域,翻過一座高山。
還有,點分樹自己腦補的板子實在太丑陋了。。這里膜拜一下ihopenot大佬,板子神快,我一開始好不容易調出來T了,參考了一下大佬的板子后就rank8了,真乃神人也!
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define N 100005 4 #define INF 1e9 5 #define lowbit(i) ((i)&-(i)) 6 int n,m,head[N],val[N],fa[N][20],dis[N][20],s[N],f[N],dep[N],rt,sz,cnt,ans; 7 bool vis[N]; 8 vector<int>bit[N],fbit[N]; 9 inline int read(){ 10 int x=0,f=1; char a=getchar(); 11 while(a>'9' || a<'0') {if(a=='-') f=-1; a=getchar();} 12 while(a<='9' && a>='0') x=x*10+a-'0',a=getchar(); 13 return x*f; 14 } 15 struct edges{ 16 int to,next; 17 }e[2*N]; 18 inline void insert(){ 19 int u=read(),v=read(); 20 e[cnt]=(edges){v,head[u]};head[u]=cnt++; 21 e[cnt]=(edges){u,head[v]};head[v]=cnt++; 22 } 23 void getroot(int x,int father){ 24 s[x]=1; f[x]=0; 25 for(int i=head[x];i>=0;i=e[i].next){ 26 if(vis[e[i].to] || father==e[i].to) continue; 27 getroot(e[i].to,x); s[x]+=s[e[i].to]; 28 f[x]=max(f[x],s[e[i].to]); 29 } 30 f[x]=max(f[x],sz-s[x]); 31 if(f[x]<f[rt]) rt=x; 32 } 33 void getship(int x,int anc,int father,int d){ 34 for(int i=head[x];i>=0;i=e[i].next){ 35 int v=e[i].to; 36 if(!vis[v] && v!=father) fa[v][++dep[v]]=anc,dis[v][dep[v]]=d,getship(v,anc,x,d+1); 37 } 38 } 39 void Buildtree(int x){ 40 vis[x]=1; getship(x,x,0,1); 41 int all=sz; bit[x].resize(all+1); fbit[x].resize(all+1); 42 for(int i=head[x];i>=0;i=e[i].next){ 43 if(vis[e[i].to]) continue; 44 sz=s[e[i].to]; if(sz>s[x]) sz=all-s[x]; 45 rt=0; getroot(e[i].to,x); Buildtree(rt); 46 } 47 } 48 inline int qsum(int x,int k){ 49 int ret=val[x],lim=bit[x].size()-1; k=min(k,lim); 50 for(int i=k;i;i-=lowbit(i)) ret+=bit[x][i]; 51 return ret; 52 } 53 inline int qsum2(int x,int k){ 54 int ret=0,lim=fbit[x].size()-1; k=min(k,lim); 55 for(int i=k;i;i-=lowbit(i)) ret+=fbit[x][i]; 56 return ret; 57 } 58 inline void change(int x,int v){ 59 int d,lim; 60 d=dis[x][dep[x]]; lim=bit[x].size()-1; 61 for(int j=d;j<=lim && j;j+=lowbit(j)) fbit[x][j]+=v; 62 for(int i=dep[x];i;i--){ 63 d=dis[x][i]; lim=bit[fa[x][i]].size()-1; 64 for(int j=d;j<=lim;j+=lowbit(j)) bit[fa[x][i]][j]+=v; 65 d=dis[x][i-1]; 66 for(int j=d;j<=lim && j;j+=lowbit(j)) fbit[fa[x][i]][j]+=v; 67 } 68 } 69 int query(int x,int k){ 70 int ret=qsum(x,k); 71 for(int i=dep[x];i;i--) if(dis[x][i]<=k) 72 ret+=qsum(fa[x][i],k-dis[x][i])-qsum2(fa[x][i+1],k-dis[x][i]); 73 return ret; 74 } 75 int main(){ 76 n=read(); m=read(); memset(head,-1,sizeof(head)); 77 for(int i=1;i<=n;i++) val[i]=read(); 78 for(int i=1;i<n;i++) insert(); 79 f[0]=INF; sz=n; getroot(1,0); Buildtree(rt); 80 for(int i=1;i<=n;i++) fa[i][dep[i]+1]=i; 81 for(int i=1;i<=n;i++) change(i,val[i]); 82 while(m--){ 83 int a=read(),b=read()^ans,c=read()^ans; 84 if(!a) ans=query(b,c),printf("%d\n",ans); 85 else change(b,c-val[b]),val[b]=c; 86 } 87 return 0; 88 }
