还是要膜拜一下hjt大佬啊->这里
又学(抄)了一个新姿势:虚点
先考虑暴力,直接从左到右link,然后T飞
可以发现如果没有1操作,所有的树的结构都是一样的(即对于询问来说并没有影响)
不难看出,离线先处理所有操作,再回答询问是没有问题的(因为操作只会在下面加节点,不会影响树上两点之间的距离)
考虑一下1操作,假设有一个形如$1 x l r$的操作,从微观上来看会有什么影响呢?先考虑第l-1棵树和第l棵树,相当于是把l-1棵树上原来生长节点的所有子节点取下来,接到l棵树上新的生长节点上去,同样,对第r棵树和第r+1棵树,相当于是把这些节点又接了回来。
现在的问题就是怎么快速转移一棵树的子树了(听说大佬们会有ETT然而蒟蒻表示听都没听过)
就是考虑建一个虚点。对于每一个1操作,我们在原来的生长节点下面建一个虚点,然后此后所有的0操作都将节点建在虚点下面,这样一来要转移子树时只需把虚点和父亲的连接断开在向右接过去就行了。于是1操作可以拆成l-1->l和r->r+1的两个操作了
一开始默认所有虚点都在一起,把所有操作离线,按端点为第一关键字,时间为第二关键字排序,从左到右处理一遍就行了
然后问题是怎么查询距离?我们可以设实点(0操作加的点)值为1,虚点值为0。然而split什么的是不可以的,因为会打乱原来的父子关系,而且他们的LCA可能是虚点。那就树上差分吧。距离就是$sum[x]+sum[y]-2*sum[LCT中的LCA]$
然而怎么在LCT求LCA?就是access(x)再access(y),access(y)过程中跳到的最后一条虚边的右儿子就是他们的LCA了(就是跳access过程中x=0时的y)(因为access(x)的时候相当于把x到根节点的路径全都标记为实边,在access(y)的时候每一次都跳一条虚边,跳完最后一条虚边就到LCA了)(大概?我也不是很清楚)
1 //minamoto 2 #include<cstdio> 3 #include<iostream> 4 #include<algorithm> 5 using std::sort; 6 #define getc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++) 7 char buf[1<<21],*p1=buf,*p2=buf; 8 template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;} 9 template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;} 10 inline int read(){ 11 #define num ch-'0' 12 char ch;bool flag=0;int res; 13 while(!isdigit(ch=getc())) 14 (ch=='-')&&(flag=true); 15 for(res=num;isdigit(ch=getc());res=res*10+num); 16 (flag)&&(res=-res); 17 #undef num 18 return res; 19 } 20 char obuf[1<<24],*o=obuf; 21 void print(int x){ 22 if(x>9) print(x/10); 23 *o++=x%10+48; 24 } 25 const int N=300005; 26 #define qr(a,b,c,d) qry[++cnt]=(q){a,b,c,d} 27 struct q{ 28 int pos,opt,x,y; 29 inline bool operator <(const q &b)const 30 {return pos<b.pos||(pos==b.pos&&opt<b.opt);} 31 }qry[N]; 32 int fa[N],ch[N][2],gl[N],gr[N],sum[N],at[N],ans[N]; 33 bool v[N]; 34 inline bool isroot(int x){return ch[fa[x]][0]!=x&&ch[fa[x]][1]!=x;} 35 #define ls ch[x][0] 36 #define rs ch[x][1] 37 inline void pushup(int x){sum[x]=sum[ls]+sum[rs]+v[x];} 38 void rotate(int x){ 39 int y=fa[x],z=fa[y],d=ch[y][1]==x; 40 if(!isroot(y)) ch[z][ch[z][1]==y]=x; 41 fa[x]=z,fa[y]=x,fa[ch[x][d^1]]=y,ch[y][d]=ch[x][d^1],ch[x][d^1]=y,pushup(y); 42 } 43 void splay(int x){ 44 for(int y=fa[x],z=fa[y];!isroot(x);y=fa[x],z=fa[y]){ 45 if(!isroot(y)) 46 ((ch[y][1]==x)^(ch[z][1]==y))?rotate(x):rotate(y); 47 rotate(x); 48 } 49 pushup(x); 50 } 51 int access(int x){ 52 int y=0; 53 for(;x;x=fa[y=x]){ 54 splay(x),rs=y,pushup(x); 55 } 56 return y; 57 } 58 void cut(int x){ 59 access(x),splay(x),ls=fa[ls]=0;pushup(x); 60 } 61 void link(int x,int y){ 62 splay(x),fa[x]=y; 63 } 64 int main(){ 65 //freopen("testdata.in","r",stdin); 66 int n,m,cnt=0,tot=0,real,aux,p; 67 n=read(),m=read(); 68 real=v[1]=sum[1]=gl[1]=at[1]=1,gr[1]=n; 69 link(p=aux=2,1); 70 for(int i=1;i<=m;++i){ 71 int op=read(),l=read(),r=read(); 72 switch(op){ 73 case 0:{ 74 link(at[++real]=++p,aux),v[p]=sum[p]=1; 75 gl[real]=l,gr[real]=r; 76 break; 77 } 78 case 1:{ 79 int x=read(); 80 cmax(l,gl[x]),cmin(r,gr[x]); 81 if(l>r) continue; 82 link(++p,aux); 83 qr(l,i-m,p,at[x]),qr(r+1,i-m,p,aux); 84 aux=p; 85 break; 86 } 87 case 2:{ 88 int x=read(); 89 qr(l,++tot,at[r],at[x]); 90 break; 91 } 92 } 93 } 94 sort(qry+1,qry+1+cnt); 95 for(int i=1;i<=cnt;++i){ 96 if(qry[i].opt>0){ 97 int x,y,l,r; 98 access(x=qry[i].x),splay(x),r=sum[x]; 99 l=access(y=qry[i].y),splay(y),r+=sum[y]; 100 access(l),ans[qry[i].opt]=r-(sum[l]<<1); 101 } 102 else cut(qry[i].x),link(qry[i].x,qry[i].y); 103 } 104 for(int i=1;i<=tot;++i) 105 print(ans[i]),*o++=' '; 106 fwrite(obuf,o-obuf,1,stdout); 107 return 0; 108 }