显然0操作的时候可以当做所有树都加了这么一个点(但是后面挂到这个点的1操作的区间要对这个区间取min),这样0和2操作都跟时间没有关系了。
可以对每个1操作建一个虚点,点权为0,然后从它到下一个1操作间的所有0操作都可以连到这个虚点。虚点先默认连向上一次1操作,一边从左到右扫一边把1操作连到对应的点或者把它连回上一个1操作,这样就可以用LCT维护出所有位置的树。
但是这样查询的时候是会出锅的。查两个点距离的时候,如果我们直接split,可能会把一个虚点当做lca,实际上还要走到上面第一个实点。
其实有根树的LCT上是可以求lca的,access的时候返回最后转到根的那个点,求的时候access(x),然后access(y)返回的点就是lca(因为lca肯定是最后一个被access的点)。
我代码写得真是太shit了。。。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int mxn=262144;
int rd(){
int x=0;
char c=getchar();
for (;c<48||c>57;c=getchar());
for (;c>47&&c<58;x=x*10+c-48,c=getchar());
return x;
}
struct nd{
int sum;
bool cnt,tag;
nd *ls,*rs,*fa;
}pool[mxn],*tpp=pool,*id[mxn],*stk[mxn];
bool noroot(nd *i){
return i->fa&&(i->fa->ls==i||i->fa->rs==i);
}
void pushup(nd *i){
i->sum=i->cnt;
if (i->ls) i->sum+=i->ls->sum;
if (i->rs) i->sum+=i->rs->sum;
}
void reverse(nd *i){
if (i) swap(i->ls,i->rs),i->tag^=1;
}
void pushdown(nd *i){
if (i->tag) i->tag=0,reverse(i->ls),reverse(i->rs);
}
void rotate(nd *i){
nd *f=i->fa,*s;
if (noroot(f))
if (f->fa->ls==f) f->fa->ls=i;
else f->fa->rs=i;
else;
i->fa=f->fa,f->fa=i;
if (f->ls==i) f->ls=s=i->rs,i->rs=f;
else f->rs=s=i->ls,i->ls=f;
if (s) s->fa=f;
pushup(f),pushup(i);
}
void splay(nd *x,int tp=1){
stk[1]=x;
for (nd *y=x;noroot(y);stk[++tp]=y=y->fa);
for (;tp;pushdown(stk[tp--]));
for (nd *y;noroot(x);rotate(x))
if (noroot(y=x->fa)) rotate((y->ls==x)^(y->fa->ls==y)?x:y);
}
nd *access(nd *x,nd *y=0){
for (;x;x=(y=x)->fa)
splay(x),x->rs=y,pushup(x);
return y;
}
void link(nd *x,nd *y){
splay(x),x->fa=y;
}
void cut(nd *x,nd *y){
access(y),splay(y),splay(x),x->fa=0;
}
int n,m,q,tot,k,lb[mxn],rb[mxn],cr[mxn],ty[mxn],ans[mxn];
struct node{
int id,pos,x,y,z;
}a[mxn];
bool operator<(const node x,const node y){
return x.pos<y.pos||(x.pos==y.pos&&x.id<y.id);
}
int main()
{
m=rd(),q=rd();
lb[1]=1,rb[1]=m,id[1]=++tpp;
n=1,m=0;
tpp->sum=tpp->cnt=1;
for (int i=1;i<=q;++i){
int typ=rd();
if (!typ){
id[++n]=++tpp,tpp->sum=tpp->cnt=1;
lb[n]=rd(),rb[n]=rd();
}
else if (typ==1)
a[++tot].x=rd(),a[tot].y=rd(),a[tot].pos=rd(),cr[++k]=n;
else ++tot,a[tot]=(node){++m,rd(),rd(),rd(),0},ty[tot]=1;
}
cr[k+1]=n,k=0;
q=tot;
for (int i=2;i<=cr[1];++i) link(id[i],id[1]);
for (int i=1,lst=1,cur=cr[1]+1;i<=tot;++i)
if (!ty[i]){
int l=max(a[i].x,lb[a[i].pos]),r=min(a[i].y,rb[a[i].pos]);
node x=(node){(l>r)*(m+1),l,++n,lst,a[i].pos};
a[i]=x;
a[++q]=(node){(l>r)*(m+1),r+1,n,a[i].z,lst};
id[n]=++tpp,link(id[n],id[lst]),lst=n;
for (++k;cur<=cr[k+1];++cur) link(id[cur],id[n]);
}
sort(a+1,a+q+1);
tot=0;
for (int i=1;i<=q;++i)
if (!a[i].id)
cut(id[a[i].x],id[a[i].y]),link(id[a[i].x],id[a[i].z]);
else if (a[i].id<=m){
access(id[a[i].x]),splay(id[1]),ans[a[i].id]+=id[1]->sum;
nd *z=access(id[a[i].y]);splay(id[1]),ans[a[i].id]+=id[1]->sum;
access(z),splay(id[1]),ans[a[i].id]-=2*id[1]->sum;
if (++tot==m) break;
}
for (int i=1;i<=m;++i) printf("%d\n",ans[i]);
return 0;
}