參考鏈接(歷史最長 霧):
http://www.cnblogs.com/New-Godess/p/4420824.html
http://blog.csdn.net/liuguangzhe1999/article/details/51124781
http://blog.csdn.net/qq_34564984/article/details/53791362
http://blog.csdn.net/popoqqq/article/details/44489859
http://blog.csdn.net/popoqqq/article/details/44461423
http://www.cnblogs.com/iamqzh/p/5521172.html
http://www.cnblogs.com/wzj-is-a-juruo/p/4632699.html
例題:poj1741 bzoj1095 bzoj3730 bzoj3924 bzoj3435
前置技能:點分治
例0 poj1741
一棵不會動的樹,詢問樹上距離<=k的點對數量。
友情提示:下文中“這坨樹”指的都是樹上的一個聯通塊,一般指的是重心管轄的這一聯通塊。
我們考慮進行點分治,對於每坨樹,我們找到重心,統計過重心的這樣的點對個數,然后對於把這坨樹從重心劈開,對於每一部分分別分治。實現細節這里略過(霧),大概就是從每個重心開始dfs,把深度排序完two-pointer一下,但是這樣對於在同一棵子樹中的會誤統計,那么就把同一棵子樹的再扣掉一遍即可。
例1 bzoj1095
還是一棵不會動的樹,每個點有一個顏色(黑色或白色),一開始全是黑色,有一些修改和詢問,修改是將某個點的顏色反轉,詢問黑點間的最長距離。
假設我們現在沒有修改,只有詢問,點分治的做法就和上面一題類似,每次分治時找到每棵子樹深度最大的一個黑點,用最大的兩個統計一下即可。
我們考慮點分治的時候把每一層的重心連向下一層重心,這樣形成了一棵以整棵樹重心為根的新樹。
我們考慮暴力一點,在每個點開兩個堆,第一個堆插入這個重心管轄的一坨樹所有黑點到分治樹上這個點父親的距離,第二個堆插入所有點分治樹上孩子的堆頂,這樣我們就可以對於每個分治重心,找到分屬兩棵子樹的深度最大的兩個黑點,即這個點堆的最大和次大值。
注意如果分治樹上的重心是白點,那么不應該用單獨一個來更新答案,如果只用一個黑點來更新答案是不合法的。
為了統計答案,我們顯然還要再開一個堆來維護答案。
修改就只要在點分治樹上,從自己那個重心往根走,維護一下那些堆即可。還是要注意上面這個更新答案不能用單獨一個。
顯然復雜度是log^2的,要在堆中刪除可以用經典trick(用另一個堆打刪除標記)。
#include <bits/stdc++.h> using namespace std; #define pb push_back typedef pair<int,int> pii; typedef vector<int> vi; #define fi first #define se second #define Edg int M=0,fst[SZ],vb[SZ],nxt[SZ];void ad_de(int a,int b){++M;nxt[M]=fst[a];fst[a]=M;vb[M]=b;}void adde(int a,int b){ad_de(a,b);ad_de(b,a);} #define esb(x,e,b) (int e=fst[x],b=vb[e];e;e=nxt[e],b=vb[e]) #define SZ 666666 struct Heap { priority_queue<int> h,d; int sz; inline void cp_() { while(d.size()&&h.top()==d.top()) h.pop(), d.pop(); } inline void push(int x) {h.push(x); ++sz;} inline void del(int x) {d.push(x); --sz;} inline int size() {return sz;} inline int top() {if(!sz) return 0; cp_(); return h.top();} inline int s2(bool c=0) { if(!sz) return 0; if(sz==1) return c?0:top(); int x=top(); h.pop(); int r=top(); h.push(x); return x+r; } inline void pop() {cp_(); h.pop(); --sz;} inline void op(int x,int o) {if(!o) push(x); else del(x);} }a,b[SZ],c[SZ]; Edg int dep[SZ],fs[SZ],ed[SZ],S=0; #define P 20 int minn[P][SZ],*ds=minn[0]; void dfs(int x,int f=0) { dep[x]=dep[f]+1; ds[fs[x]=ed[x]=++S]=dep[x]; for esb(x,e,b)if(b!=f) { dfs(b,x); ds[ed[x]=++S]=dep[x]; } } int l2[SZ]; void pre() { l2[0]=-1; for(int i=1;i<=S;i++) l2[i]=l2[i>>1]+1; for(int i=1;i<P;i++) { int*a=minn[i-1],*b=minn[i]; for(int j=1;j+(1<<i)-1<=S;j++) b[j]=min(a[j],a[j+(1<<(i-1))]); } } int dis(int a,int b) { if(fs[a]>fs[b]) swap(a,b); int l=fs[a],r=ed[b],x=l2[r-l+1]; int mi=min(minn[x][l],minn[x][r-(1<<x)+1]); return dep[a]+dep[b]-mi*2; } int dfa[SZ],sz[SZ],ms[SZ]; bool vis[SZ]; vi sons[SZ],tmp; int root=0,sum; void gr(int x,int f=0) { tmp.pb(x); sz[x]=1; ms[x]=0; for esb(x,e,b) { if(b==f||vis[b]) continue; gr(b,x); sz[x]+=sz[b]; ms[x]=max(ms[x],sz[b]); } ms[x]=max(ms[x],sum-sz[x]); if(ms[x]<ms[root]||!root) root=x; } int n,col[SZ]; int dfz(int s=n,int bk=1,int df=0) { sum=s;root=0;tmp.clear();gr(bk); int x=root;sons[x]=tmp;dfa[x]=df; if(df) { for(int i=0;i<tmp.size();i++) c[x].push(dis(tmp[i],df)); } vis[x]=1; for esb(x,e,g) { if(vis[g]) continue; int r=dfz(sz[g],g,x); b[x].push(c[r].top()); } a.push(b[x].s2()); return x; } void flip(int x) { int ov2=b[x].s2(!col[x]),nv2=b[x].s2(col[x]); if(ov2!=nv2) {a.del(ov2); a.push(nv2);} for(int y=x;y;y=dfa[y]) { int z=dfa[y],ov=c[y].top(); if(!z) continue; c[y].op(dis(x,z),col[x]); int nv=c[y].top(); if(nv==ov) continue; int ov2=b[z].s2(col[z]); if(ov) b[z].del(ov); if(nv) b[z].push(nv); int nv2=b[z].s2(col[z]); if(ov2!=nv2) {a.del(ov2); a.push(nv2);} } } #define S myS char ch,B[1<<15],*S=B,*T=B; #define getc() (S==T&&(T=(S=B)+fread(B,1,1<<15,stdin),S==T)?0:*S++) #define isd(c) (c>='0'&&c<='9') int aa,bb;int F(){ while(ch=getc(),!isd(ch)&&ch!='-');ch=='-'?aa=bb=0:(aa=ch-'0',bb=1); while(ch=getc(),isd(ch))aa=aa*10+ch-'0';return bb?aa:-aa; } #define gi F() int main() { n=gi; int tot=n; for(int i=1;i<n;i++) adde(gi,gi); dfs(1); pre(); dfz(); int q=gi; while(q--) { char s=getc(); if(s=='G') { if(tot) printf("%d\n",a.top()); else puts("-1"); continue; } int x=gi; if(col[x]) ++tot; else --tot; col[x]^=1; flip(x); } }
此外還有一種基於括號序列的做法,先mark,改天學
例2 bzoj3924
還是一棵不會動的樹,邊有長度,每個點i有一個點權di,開始都為0,有q次對某個點點權的修改,每次修改之后你需要選出一個點s,最小化$\sum_vd_vdis(s,v)$並輸出。
如果所有d都是1,我們可以發現這就是要求重心,那么有了d就是要求帶修改帶權重心。
首先我們假裝沒有修改,詢問某些固定的s,要如何計算這個給定值?
那對於一個s和每個v,對於每個重心w,如果s到v的路徑經過w,那么dis(s,v)=dis(s,w)+dis(w,v),dis(s,w)是定值,我們預先統計d[v]之和乘上去即可,所有dis(w,v)d[v]也可以預先統計。
那么考慮在點分治樹從s往上往上爬,假設從重心a爬到了重心b,那么b這一坨樹去掉a這一坨樹,里面的點到s的路徑都經過b,然后直接統計即可。
這樣搞顯然是可以支持修改的,只要同樣往上爬並更新信息即可。
那么現在問題就是要確定這個s,怎么確定...我也不是很清楚啊。網上的題解都是隨便找個點,然后如果有一棵子樹答案比它小就往那邊走。這不是隨便卡嗎(復雜度不對)...所以代碼就不寫了(大霧)等我會了再來填坑
UPD:好像可以把復雜度改對,如果一個點有多個孩子,可以把孩子建成線段樹那樣,強制把每個點變成不超過兩個孩子,這樣走復雜度聽說就對了。
例3 bzoj3730
又是一棵不會動的樹,每個點有點權,有m次操作或詢問,強制在線。
操作是改變某個點的點權,詢問是詢問和某個點距離不超過某個值的點權和。
這題和例2沒啥區別吧...仍然考慮在點分治樹從s往上往上爬,假設從重心a爬到了重心b,那么b這一坨樹去掉a這一坨樹,里面的點到s的路徑仍然都經過b,考慮dis(s,v)=dis(s,w)+dis(w,v)<=k,對於我們dis(s,w)是定值,那么我們只要維護dis(w,v)<=某個值的點權和就好了。
因為dis(w,v)對於每個點是定值,只要開一堆vector二分+樹狀數組即可。為了要“去掉a這一坨樹”,每個重心不僅要統計孩子信息,還要統計到父親的信息。
下面這份代碼為了節約空間,sons和sons2數組開始記的是管轄點,最后記距離。因為stl,跑的十分慢...
#include <bits/stdc++.h> using namespace std; #define pb push_back typedef pair<int,int> pii; typedef vector<int> vi; #define fi first #define se second #define Edg int M=0,fst[SZ],vb[SZ],nxt[SZ];void ad_de(int a,int b){++M;nxt[M]=fst[a];fst[a]=M;vb[M]=b;}void adde(int a,int b){ad_de(a,b);ad_de(b,a);} #define esb(x,e,b) (int e=fst[x],b=vb[e];e;e=nxt[e],b=vb[e]) #define SZ 666666 Edg int dep[SZ],fs[SZ],ed[SZ],S=0; #define P 20 int minn[P][SZ],*ds=minn[0]; void dfs(int x,int f=0) { dep[x]=dep[f]+1; ds[fs[x]=ed[x]=++S]=dep[x]; for esb(x,e,b)if(b!=f) { dfs(b,x); ds[ed[x]=++S]=dep[x]; } } int l2[SZ]; void pre() { l2[0]=-1; for(int i=1;i<=S;i++) l2[i]=l2[i>>1]+1; for(int i=1;i<P;i++) { int*a=minn[i-1],*b=minn[i]; for(int j=1;j+(1<<i)-1<=S;j++) b[j]=min(a[j],a[j+(1<<(i-1))]); } } int dis(int a,int b) { if(fs[a]>fs[b]) swap(a,b); int l=fs[a],r=ed[b],x=l2[r-l+1]; int mi=min(minn[x][l],minn[x][r-(1<<x)+1]); return dep[a]+dep[b]-mi*2; } int dfa[SZ],sz[SZ],ms[SZ],cmpd[SZ]; bool vis[SZ]; vi sons[SZ],pos[SZ],bt[SZ],tmp; vi sons2[SZ],pos2[SZ],bt2[SZ]; int query(vi& v,int x) { int ans=0; for(++x;x;x-=x&-x) ans+=v[x-1]; return ans; } void edt(vi& v,int x,int y) { for(++x;x<=v.size();x+=x&-x) v[x-1]+=y; } int bs(vi& v,int g) { return min(int(upper_bound(v.begin(),v.end(),g)-v.begin()-1), (int)v.size()-1); } int root=0,sum; void gr(int x,int f=0) { tmp.pb(x); sz[x]=1; ms[x]=0; for esb(x,e,b) { if(b==f||vis[b]) continue; gr(b,x); sz[x]+=sz[b]; ms[x]=max(ms[x],sz[b]); } ms[x]=max(ms[x],sum-sz[x]); if(ms[x]<ms[root]||!root) root=x; } bool cmp(int a,int b) {return cmpd[a]<cmpd[b];} int n,m,val[SZ]; void dfz(int s=n,int bk=1,int df=0) { sum=s;root=0;tmp.clear();gr(bk); int x=root;sons[x]=tmp;dfa[x]=df; for(int i=0;i<tmp.size();i++) cmpd[tmp[i]]=dis(tmp[i],x); sort(sons[x].begin(),sons[x].end(),cmp); bt[x].resize(tmp.size()); for(int i=0;i<tmp.size();i++) pos[sons[x][i]].pb(i), edt(bt[x],i,val[sons[x][i]]); for(int i=0;i<tmp.size();i++) sons[x][i]=cmpd[sons[x][i]]; //前方高能 if(df) { sons2[x]=tmp; for(int i=0;i<tmp.size();i++) cmpd[tmp[i]]=dis(tmp[i],df); sort(sons2[x].begin(),sons2[x].end(),cmp); bt2[x].resize(tmp.size()); for(int i=0;i<tmp.size();i++) pos2[sons2[x][i]].pb(i), edt(bt2[x],i,val[sons2[x][i]]); for(int i=0;i<tmp.size();i++) sons2[x][i]=cmpd[sons2[x][i]]; } vis[x]=1; for esb(x,e,g) if(!vis[g]) dfz(sz[g],g,x); } void upd(int x,int dv) { for(int y=x,*z=&pos[x][pos[x].size()-1] ,*z2=&pos2[x][pos2[x].size()-1];y; y=dfa[y],--z,--z2) { edt(bt[y],*z,dv); if(dfa[y]) edt(bt2[y],*z2,dv); } } int qd(int x,int k) { int ans=0; for(int y=x,bk=0;y;bk=y,y=dfa[y]) { int d=k-dis(x,y), ca=query(bt[y],bs(sons[y],d)); if(bk) ca-=query(bt2[bk],bs(sons2[bk],d)); ans+=ca; } return ans; } #define S myS char ch,B[1<<15],*S=B,*T=B; #define getc() (S==T&&(T=(S=B)+fread(B,1,1<<15,stdin),S==T)?0:*S++) #define isd(c) (c>='0'&&c<='9') int aa,bb;int F(){ while(ch=getc(),!isd(ch)&&ch!='-');ch=='-'?aa=bb=0:(aa=ch-'0',bb=1); while(ch=getc(),isd(ch))aa=aa*10+ch-'0';return bb?aa:-aa; } #define gi F() int main() { n=gi,m=gi; for(int i=1;i<=n;i++) val[i]=gi; for(int i=1;i<n;i++) adde(gi,gi); dfs(1); pre(); dfz(); int lans=0; while(m--) { int a=gi,b=gi^lans,c=gi^lans; if(!a) { printf("%d\n",lans=qd(b,c)); continue; } upd(b,c-val[b]); val[b]=c; } }
例4 紫荊花之戀
咳咳,這題就先暫時坑在這里...為什么大家應該都懂。