发现一个重要的性质:由于不存在删点的操作,所以之后的加点并不会对之前的询问的答案造成影响。
所以我们可以先处理修改,再处理询问。
考虑从左到右扫描每一颗树,每次将前一棵树通过一些修改变为后一棵树。
发现我们修改一段区间的生长点的位置,相当于在扫描到这个区间的左端点时,将接在原来生长点下面的子树全部转移到新的生长点下面,在扫描右端点结束后,将新的生长点下面的子树移回去。
如下图(红色为原来生长点,蓝色为修改后生长点)
->
但是这样不方便对子树进行转移,我们想到可以将一棵子树的转移变为一个点的转移,然后用LCT维护。
于是我们在每次的生长点的孩子位置建一个虚点,将所有从生长点生长出来的点接在虚点的孩子上。
这样一来,我们转移子树的时候,只需要切断虚点和它的父亲的边,再把它连到新的生长点上就好了。
如下图
->
我们用((1,p,x,y))表示在第(p)棵树上,将虚点(x)移动到点(y)的孩子上的操作。
然后,我们对于每一个1操作(1 l r y),将它拆成两个操作:
((1,l,x,y))将一个虚点(x)在左端点(l)的树上移动到(y)的孩子上(关于虚点(x)从哪里来之后会讲);
((1,r+1,x,li))将一个虚点(x)在右端点后一个点(r+1)的树上移动到(x)被建立时的上一个虚点(li)的孩子上(之后会讲),即撤销之前在左端点上的移动。
然后考虑具体实现
一开始,我们维护第0棵树,即所有的节点都生长在1号节点的孩子上。
初始有两个节点:实点1和虚点2,2是1的孩子。
对于一个0操作,我们新建一个节点,同时维护一个从实点集合到节点集合的映射(mp),(mp_i)表示第(i)个实点是第几个节点,将它连接在目前上一个被建立的虚点上,并同时记录存在第(i)个实点的树的区间([sgl_i,sgr_i])。
对于一个1操作,我们新建一个虚点(x),执行上述操作。
对于一个2操作,我们用((2,p,x,y))表示询问第(p)棵树上,节点(注意不是实点)(x)和(y)的距离。
注意,此时我们并没有执行由(1)操作和(2)操作获得的操作和询问。
然后,我们要对所有的操作排序,第一关键字为操作或者询问的位置(位置靠前的排在前面),第二关键字为询问或操作(询问排在操作前面)
然后对于一个操作,我们断开需要移动的点和它原来的父亲,然后将它连在新父亲的孩子上。
对于一个询问,我们运用树上差分,给实点赋权为1,虚点赋权为0(这一步在建立节点时就可以实现),记录(sum_x)表示节点(x)到1路径上节点的权值和,那么节点(i,j)之间的路径长度=(sum_i+sum_j-sum_{LCA(i,j)})。
最后一个问题,如何在LCT上求LCA?
显然我们不能(Split)(即进行(Makeroot)然后(Access)),因为这是一棵有根(1)树,这样会改变它的结构。
于是我们对于节点(i,j)分别进行(Access),后一次中最后一个与右孩子切断实边的节点即为LCA。
为什么?其实最后一次切断目前节点与右孩子的实边后,目前节点到根的部分即为两点到根路径的公共部分,自然这个点就是(i)和(j)的LCA。
code:
#include<bits/stdc++.h>
#define ci const int&
using namespace std;
struct query{
int op,pos,x,y,id;//move x to son of y(op=1),query the lenth of (x,y)(op=2)
}q[400010];
struct node{
int f,c[2],tag,val,sum;
}t[200010];
int n,m,sz,tt,T,len,op,a,b,c,cnt,li,mp[200010],rn,sgl[200010],sgr[200010],prt[200010];
bool cmp(query x,query y){
return x.pos!=y.pos?x.pos<y.pos:x.op<y.op;
}
void Upd(ci x){
t[x].sum=t[t[x].c[0]].sum+t[t[x].c[1]].sum+t[x].val;
}
void Psd(ci x){
t[x].tag?swap(t[x].c[0],t[x].c[1]),t[t[x].c[0]].tag^=1,t[t[x].c[1]].tag^=1,t[x].tag=0:0;
}
bool Isroot(ci x){
return t[t[x].f].c[0]!=x&&t[t[x].f].c[1]!=x;
}
void Rotate(ci x){
int y=t[x].f,z=t[y].f;
Psd(y),Psd(x);
int c=t[y].c[1]==x,gc=t[z].c[1]==y;
!Isroot(y)?t[z].c[gc]=x:0,t[x].f=z;
t[y].c[c]=t[x].c[c^1],t[t[x].c[c^1]].f=y;
t[x].c[c^1]=y,t[y].f=x;
Upd(y),Upd(x);
}
void Splay(ci x){
while(!Isroot(x)){
int y=t[x].f,z=t[y].f;
if(Isroot(y))Rotate(x);
else{
Psd(z),Psd(y);
(t[y].c[1]==x)==(t[z].c[1]==y)?Rotate(y):Rotate(x),Rotate(x);
}
}
}
int Access(ci x,ci lst){
if(!x)return 0;
Splay(x),t[x].c[1]=lst,Upd(x);
int tmp=Access(t[x].f,x);
return tmp?tmp:x;
}
void Cut(ci x){
Access(x,0),Splay(x),t[x].c[0]=t[t[x].c[0]].f=0,Upd(x);
}
void Link(ci x,ci y){
Splay(y),t[y].f=x;
}
void printpath(int x){
if(!x)return;
Splay(x);
t[x].c[0]?printpath(t[x].c[0]):printpath(t[x].f);
}
int main(){
scanf("%d%d",&n,&m);
rn=1,li=cnt=2,t[1].val=t[1].sum=1,Link(1,2),mp[1]=sgl[1]=1,sgr[1]=n;
while(m--){
scanf("%d",&op);
if(op==0)scanf("%d%d",&a,&b),mp[++rn]=++cnt,sgl[rn]=a,sgr[rn]=b,Link(li,cnt),t[cnt].val=t[cnt].sum=1;
else if(op==1){
scanf("%d%d%d",&a,&b,&c),a=max(a,sgl[c]),b=min(b,sgr[c]);
if(a>b)continue;
Link(li,++cnt),q[++sz]=(query){1,a,cnt,mp[c],0},q[++sz]=(query){1,b+1,cnt,li,0},li=cnt;
}else ++sz,scanf("%d%d%d",&q[sz].pos,&q[sz].x,&q[sz].y),q[sz].x=mp[q[sz].x],q[sz].y=mp[q[sz].y],q[sz].op=2,q[sz].id=++tt;
}
sort(q+1,q+sz+1,cmp);
for(int i=1;i<=sz;++i){
if(q[i].op==1)Cut(q[i].x),Link(q[i].y,q[i].x);
else Access(q[i].x,0),Splay(q[i].x),len=0,len+=t[q[i].x].sum,T=Access(q[i].y,0),Splay(q[i].y),len+=t[q[i].y].sum,Access(T,0),prt[q[i].id]=len-(t[T].sum<<1);
}
for(int i=1;i<=tt;++i)printf("%d
",prt[i]);
return 0;
}