题目大意
初始给出n棵节点数为1且生长节点标号为1的树,有三个操作:
0 l r 表示将第 l 棵树到第 r 棵树的生长节点下面长出一个子节点,标号为当前0的操作数+1;
1 l r x 表示将第 l 棵树到第 r 棵树的生长节点改到标号为 x 的节点。对于 i (l≤i≤r)这棵树,如果标号 x的点不在其中,那么这个操作对该树不产生影响;
2 x u v 询问第 x 棵树中节点 u 到节点 v 点的距离。
N<=10^5,M<=2*10^5
离线,lct
先定义实点值为1,虚点值为0
对于每一个生长节点,我们新建一个虚点;一个0操作,在对应的虚点下面建一个实点。
1操作可能存在不合法的部分,但由于0操作是对于一个区间修改的,那么1操作的修改区间显然也是连续的,很容易得到实际要修改的区间,不需要考虑不合法情况。
1操作在l位置挂一个插入,在r+1位置挂一个删除。
对于插入操作,我们直接把新建节点向x连条边;
对于删除操作,我们把新建节点和x的边删掉,再向上一个生长节点的虚点连条边。
可以机智的发现,0操作其实可以一开始就全处理掉,并不会影响答案。
以上用lct就可以解决。
代码
#include<cstring>
#include<algorithm>
#include<cstdio>
#define ll long long
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fod(i,a,b) for(i=a;i>=b;i--)
#define max(a,b) (a>b?a:b)
#define min(a,b) (a<b?a:b)
using namespace std;
char c;int w;
inline int read(){
w=0;
for(c=getchar();c<'0'||c>'9';c=getchar());
for(;c>='0'&&c<='9';c=getchar()) w=w*10+c-48;
return w;
}
const int maxn=4e5+5;
struct ar{
int pos,op,x,y;
}d[maxn];
bool cmp(ar x,ar y){
return x.pos<y.pos||(x.pos==y.pos&&x.op<y.op);
}
int i,j,n,m,q[maxn],cn,now,last,b[maxn],tree[maxn][2],sum[maxn],num[maxn],fa[maxn],tfa[maxn];
int ans[maxn],l[maxn],cnt=1,r[maxn],tot;
bool bz[maxn];
bool pd(int x) {return (tree[fa[x]][0]==x)?0:1;}
void re(int x){
if (!x)return;
swap(tree[x][0],tree[x][1]),bz[x]^=1;
}
void ins(int x){num[++cn]=sum[cn]=x;}
void update(int x){
int l=tree[x][0],r=tree[x][1];
sum[x]=sum[l]+sum[r]+num[x];
}
void rotate(int x){
int y=fa[x],z=pd(x),z1=pd(y);
if (tree[y][z]=tree[x][z^1]) fa[tree[y][z]]=y;
if (fa[x]=fa[y]) tree[fa[x]][z1]=x;
if (tfa[y]) tfa[x]=tfa[y],tfa[y]=0;
tree[x][z^1]=y,fa[y]=x,update(y);
}
void clear(int x){
int l=tree[x][0],r=tree[x][1];
if (bz[x]) re(l),re(r);
bz[x]=0;
}
void chu(int x,int y){
q[0]=0;
for(;x!=y;x=fa[x]) q[++q[0]]=x;
for(;q[0];q[0]--) clear(q[q[0]]);
}
void splay(int x,int y){
chu(x,y);
while (fa[x]!=y){
int f=fa[x];
if (fa[f]!=y){
if (pd(x)==pd(f)) rotate(f);else rotate(x);
}rotate(x);
}update(x);
}
void access(int x){
last=0;int y;
for(;x;last=x,x=tfa[x]){
splay(x,0);
y=tree[x][1];
if (y) tree[x][1]=0,fa[y]=0,tfa[y]=x;
if (last) tree[x][1]=last,fa[last]=x,tfa[last]=0;
update(x);
}
}
void makeroot(int x){
access(x);
splay(x,0);
re(x);
}
void link(int x,int y){
makeroot(x);
splay(x,0);
tfa[x]=y;
}
void cut(int x){
access(x);
splay(x,0);
tree[x][0]=fa[tree[x][0]]=0;
update(x);
}
int main(){
n=read(),m=read();
ins(1),b[1]=l[1]=1,r[1]=n;
ins(0),now=2,link(2,1);
fo(i,1,m){
int o=read();
if (!o){
l[++cnt]=read(),r[cnt]=read();
ins(1),b[cnt]=cn;
d[++tot]=(ar){1,i-m,cn,now};
}else
if (o==1){
int x=read(),y=read(),k=read();
x=max(x,l[k]),y=min(y,r[k]);
if (x<=y){
ins(0);
link(cn,now);
d[++tot]=(ar){x,i-m,cn,b[k]};
d[++tot]=(ar){y+1,i-m,cn,now};
now=cn;
}
}else{
int x=read(),u=read(),v=read();
d[++tot]=(ar){x,i,b[u],b[v]};
}
}
sort(d+1,d+1+tot,cmp);
memset(ans,-1,sizeof(ans));
j=1;
fo(i,1,n){
for(;j<=tot&&d[j].pos==i;j++)if (d[j].op>0){
int x=d[j].x,y=d[j].y;
access(x),splay(x,0),ans[d[j].op]=sum[x];
access(y),splay(y,0),ans[d[j].op]+=sum[y];
access(last),splay(last,0),ans[d[j].op]-=sum[last]<<1;
}else cut(d[j].x),link(d[j].x,d[j].y);
}
fo(i,1,m) if (ans[i]!=-1) printf("%d\n",ans[i]);
}