1. 程式人生 > >【雜題】[BZOJ4573][UOJ#195]【ZJOI2016】大森林【資料結構】【LCT】

【雜題】[BZOJ4573][UOJ#195]【ZJOI2016】大森林【資料結構】【LCT】

Description

小Y家裡有一個大森林,裡面有 n 棵樹,編號從 1到 n 。一開始這些樹都只是樹苗,只有一個節點,標號為 1 。這些樹都有一個特殊的節點,我們稱之為生長節點,這些節點有生長出子節點的能力。

小Y掌握了一種魔法,能讓第 l 棵樹到第 r棵樹的生長節點長出一個子節點。同時她還能修改第 l 棵樹到第 r棵樹的生長節點。

她告訴了你她使用魔法的記錄,你能不能管理她家的森林,並且回答她的詢問呢?
第一行包含 2 個正整數 n,m,共有 n 棵樹和 m 個操作。接下來 m 行,每行包含若干非負整數表示一個操作,操作格式為:
0 l r 表示將第 l 棵樹到第 r 棵樹的生長節點下面長出一個子節點,子節點的標號為上一個 0 號操作葉子標號加 1(例如,第一個 0 號操作產生的子節點標號為 2), l 到 r 之間的樹長出的節點標號都相同。保證 1≤l≤r≤n 。
1 l r x 表示將第 l 棵樹到第 r 棵樹的生長節點改到標號為 x 的節點。對於 i (l≤i≤r)這棵樹,如果標號 x 的點不在其中,那麼這個操作對該樹不產生影響。保證 1≤l≤r≤n , x 不超過當前所有樹中節點最大的標號。
2 x u v 詢問第 x 棵樹中節點 u 到節點 v 點的距離,也就是在第 x 棵樹中從節點 u 和節點 v 的最短路上邊的 數量。保證1≤x≤n,這棵樹中節點 u 和節點 v 存在。

N<=10^5, M<=2*10^5

Solution

首先可以確定的是,詢問結果不會隨著時間改變而改變,那麼詢問時間就不重要了。

另外對於一個更改生長節點操作,我們要將這個區間與更改到的節點出現的那次0操作區間取並

接下來我們將所有的0/1操作拆成左端點和右端點兩個,從左到右掃

考慮LCT,一個加入0操作就是link,刪除0操作就是cut,重點在於1操作,會將後面一堆點一起移動。

我們可以對於每一個1操作建一個虛點,記錄所有的0操作前最晚的1操作是什麼。一開始所有的1操作虛點連向它前一個1操作的虛點,當加入0操作就將這個點連向前面最晚的一個虛點。加入1操作就相應的將虛點原本連向前一個1操作的邊刪掉,連到對應點去,刪除1操作就是一個逆過程。

注意可能出現一種情況是查詢路徑時可能虛點是LCA,這樣就少算了1,因此我們要將邊轉點,後面的實點連向前面的虛點邊權為1,虛點連虛點以及虛點連向前面的生長節點的邊權為0。

時間複雜度O(m log n)

Code

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdlib>
#include <cmath>
#define fo(i,a,b) for(int i=a;i<=b;++i)
#define fod(i,a,b) for(int i=a;i>=b;--i) #define N 600005 using namespace std; struct LCT { int fn[N],pr[N],sm[N],t[N][2],f[N],d[N],r[N],n1; bool bz[N]; void hb(int p,int x,int y) { if(x&&p>-1) t[x][p]=y; if(y) f[y]=x,fn[y]=p; } void up(int k) { sm[k]=sm[t[k][0]]+sm[t[k][1]]+pr[k]; } void down(int k) { r[0]=0; if(r[k]) swap(t[k][0],t[k][1]),fn[t[k][0]]=0,fn[t[k][1]]=1,fn[0]=-1; if(t[k][0]) r[t[k][0]]^=r[k]; if(t[k][1]) r[t[k][1]]^=r[k]; r[k]=0; } void rot(int k) { int fa=f[k],p=fn[k]; hb(p,fa,t[k][1-p]); hb(fn[fa],f[fa],k); hb(1-p,k,fa); up(fa),up(k),up(f[k]); } void splay(int k,int x) { d[d[0]=1]=k; while(f[d[d[0]]]!=x&&fn[d[d[0]]]!=-1&&f[d[d[0]]]!=0) d[++d[0]]=f[d[d[0]-1]]; fod(i,d[0],1) down(d[i]); while(f[k]!=x&&fn[k]!=-1&&f[k]!=0) { if(f[f[k]]==x||fn[f[k]]==-1||f[k]==0) rot(k); else if(fn[k]==fn[f[k]]) rot(f[k]),rot(k); else rot(k),rot(k); } } void access(int k) { int p=k;splay(k,0); fn[t[k][1]]=-1,t[k][1]=0,up(k); while(f[k]!=0) { splay(f[k],0); fn[t[f[k]][1]]=-1,hb(1,f[k],k); k=f[k],up(k); } splay(p,0); } void make(int k) { access(k),r[k]^=1; } void link(int x,int y) { make(x),access(y); f[x]=y,fn[x]=-1; } void cut(int x,int y) { make(x),access(y); splay(x,0),splay(y,x); f[t[x][1]]=f[t[y][0]]=0; t[x][1]=t[y][0]=0; fn[y]=-1,f[y]=0; access(y); } void lk(int x,int y,int p) { n1++; pr[n1]=p; link(x,n1),link(y,n1); } }T; struct node { int x,p,u,v,w; friend bool operator <(node x,node y) { return (x.x<y.x)||(x.x==y.x&&y.p==2); } }op[N]; int n,m,fr[N],ans[N],nq,lt[N][2]; int main() { cin>>n>>m; int num=1,qs=0,ls=m+1; lt[1][0]=1,lt[1][1]=n; fo(i,2,m) lt[i][0]=lt[i][1]=n+1; fo(i,1,m) { int p,l,r,x; scanf("%d%d%d",&p,&l,&r); if(p==0) { fr[++num]=ls; op[++nq]={l,p,num,1,i}; op[++nq]={r+1,p,num,-1,i}; lt[num][0]=l,lt[num][1]=r; } else if(p==1) { scanf("%d",&x); l=max(l,lt[x][0]),r=min(r,lt[x][1]); if(l<=r) { T.pr[++ls]=0; T.link(ls-1,ls); op[++nq]={l,p,x,1,ls}; op[++nq]={r+1,p,x,-1,ls}; } } else { scanf("%d",&x); op[++nq]={l,p,r,x,++qs}; } } T.n1=ls; sort(op+1,op+nq+1); int j=1; T.link(1,m+1); fo(i,1,n) { while(j<=nq&&op[j].x<=i) { if(op[j].p==0) { if(op[j].v==1) T.lk(fr[op[j].u],op[j].u,1); else T.cut(fr[op[j].u],op[j].u); } else if(op[j].p==1) { if(op[j].v==1) T.cut(op[j].w-1,op[j].w),T.link(op[j].u,op[j].w); else T.cut(op[j].u,op[j].w),T.link(op[j].w-1,op[j].w); } else { T.make(op[j].u),T.access(op[j].v); ans[op[j].w]=T.sm[op[j].v]; } j++; } } fo(i,1,qs) printf("%d\n",ans[i]); }