【雜題】[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]);
}