小Y家里有一个大森林,里面有 n n 棵树,编号从 1 1 到 n n 。一开始这些树都只是树苗,只有一个节点,标号为 1 1 。这些树都有一个特殊的节点,我们称之为生长节点,这些节点有生长出子节点的能力。
小Y掌握了一种魔法,能让第 l l 棵树到第 r r 棵树的生长节点长出一个子节点。同时她还能修改第 l l 棵树到第 r r 棵树的生长节点。
她告诉了你她使用魔法的记录,你能不能管理她家的森林,并且回答她的询问呢?
输入格式
第一行包含 2 个正整数 n,m n , m ,共有 n n 棵树和 m m 个操作。
接下来 m m 行,每行包含若干非负整数表示一个操作,操作格式为:
- 0 0 l l r r 表示将第 l l 棵树到第 r r 棵树的生长节点下面长出一个子节点,子节点的标号为上一个 0 0 号操作叶子标号加 1 1 (例如,第一个 0 0 号操作产生的子节点标号为 2 2 ), l l 到 r r 之间的树长出的节点标号都相同。保证 1≤l≤r≤n 1 ≤ l ≤ r ≤ n 。
- 1 1 l l r r x x 表示将第 l l 棵树到第 r r 棵树的生长节点改到标号为 x x 的节点。对于区间内的每棵树,如果标号 x x 的点不在其中,那么这个操作对该树不产生影响。保证 1≤l≤r≤n 1 ≤ l ≤ r ≤ n , x x 不超过当前所有树中节点最大的标号。
- 2 2 x x u u v v 询问第 x x 棵树中节点 u u 到节点 v v 的距离,也就是在第 x x 棵树中从节点 u u 和节点 v v 的最短路上边的数量。保证 1≤x≤n 1 ≤ x ≤ n ,这棵树中节点 u u 和节点 v v 存在。
输出格式
输出包括若干行,按顺序对于每个小Y的询问输出答案。
样例一
5 5
0 1 5
1 2 4 2
0 1 4
2 1 1 3
2 2 1 3
output
1
2
样例二
见样例数据下载。
限制与约定
测试点编号 |
n n |
m m |
约定 |
1 |
≤103 ≤ 10 3 |
≤103 ≤ 10 3 |
|
2 |
≤105 ≤ 10 5 |
≤2×105 ≤ 2 × 10 5 |
保证每次 0 0 和 1 1 操作修改的是 1 1 到 n n 所有的树 |
3 |
4 |
保证每次 0 0 操作生长节点都是这些树中编号最大的节点 |
5 |
6 |
|
7 |
8 |
9 |
10 |
时间限制: 2s 2 s
空间限制: 256MB 256 MB
思路&&分析:
对于每一个1操作,我们可以建一个虚点,然后对于每个0操作都将这些点挂在[在这个操作前面最迟的1操作上建的虚点]的下面。于是我们可以将操作离线,然后从左到右把所有树都扫一遍就行了,这样一来我们打一颗LCT就完事了。
Code
#pragma GCC optimize(3)
#include
using namespace std;
typedef long long ll;
bool Finish_read;
template<class T>inline void read(T &x){Finish_read=0;x=0;int f=1;char ch=getchar();while(!isdigit(ch)){if(ch=='-')f=-1;if(ch==EOF)return;ch=getchar();}while(isdigit(ch))x=x*10+ch-'0',ch=getchar();x*=f;Finish_read=1;}
template<class T>inline void print(T x){if(x/10!=0)print(x/10);putchar(x%10+'0');}
template<class T>inline void writeln(T x){if(x<0)putchar('-');x=abs(x);print(x);putchar('\n');}
template<class T>inline void write(T x){if(x<0)putchar('-');x=abs(x);print(x);}
const int maxn=200005;
int c[maxn][2],sz[maxn],val[maxn],fa[maxn],p,cnt,m,n,tot,le[maxn],ri[maxn],id[maxn],now,ans[maxn];
struct Event {
int pos,op,x,y;
Event(){}
Event(const int &pos,const int &op,const int &x,const int &y):pos(pos),op(op),x(x),y(y){}
inline bool operator < (const Event &rhs) const {
return pos==rhs.pos?op2];
inline void pushup(int x) {
int l=c[x][0],r=c[x][1];
sz[x]=sz[l]+sz[r]+val[x];
}
inline bool isrt(int x) {
return c[fa[x]][0]!=x&&c[fa[x]][1]!=x;
}
inline void rotate(int x) {
int y=fa[x],z=fa[y],l=(c[y][1]==x),r=l^1;
if(!isrt(y))
c[z][c[z][1]==y]=x;
fa[y]=x,fa[x]=z;
fa[c[x][r]]=y;
c[y][l]=c[x][r];
c[x][r]=y;
pushup(y),pushup(x);
}
inline void splay(int x) {
while(!isrt(x)) {
int y=fa[x],z=fa[y];
if(!isrt(y)) {
if(c[y][0]==x^c[z][0]==y)
rotate(x);
else
rotate(y);
}
rotate(x);
}
}
inline int access(int x) {
int t=0;
for(;x;t=x,x=fa[x])
splay(x),c[x][1]=t,pushup(x);
return t;
}
inline void cut(int x) {
access(x);
splay(x);
c[x][0]=fa[c[x][0]]=0;
pushup(x);
}
inline void link(int x,int y) {
splay(x);
fa[x]=y;
}
inline void add(int o) {
sz[n+1]=val[++n]=o;
}
int main() {
read(p),read(m);
add(1),cnt=1,le[cnt]=id[cnt]=1,ri[cnt]=p,add(0),now=2,link(2,1);
memset(ans,-1,sizeof ans);
for(int i=1,op,k,x,y;i<=m;i++) {
read(op);
if(op==0) {
read(x);read(y);++cnt;
le[cnt]=x,ri[cnt]=y,add(1),id[cnt]=n;
a[++tot]=Event(1,i-m,n,now);
}
else if(op==1) {
read(x),read(y),read(k);
x=max(x,le[k]),y=min(y,ri[k]);
if(x<=y) {
add(0);
if(x>1)
link(n,now);
a[++tot]=Event(x,i-m,n,id[k]);
a[++tot]=Event(y+1,i-m,n,now);
now=n;
}
}
else {
read(k),read(x),read(y);
a[++tot]=Event(k,i,id[x],id[y]);
}
}
sort(a+1,a+tot+1);
for(int i=1,k=1;i<=p;i++)
for(;k<=tot&&a[k].pos==i;k++)
if(a[k].op>0) {
access(a[k].x),splay(a[k].x),ans[a[k].op]=sz[a[k].x];
int t=access(a[k].y);splay(a[k].y),ans[a[k].op]+=sz[a[k].y];
access(t),splay(t),ans[a[k].op]-=sz[t]<<1;
}
else {
cut(a[k].x);
link(a[k].x,a[k].y);
}
for(int i=1;i<=m;i++)
if(ans[i]!=-1)
printf("%d\n",ans[i]);
return 0;
}