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]);
}