题目描述
小Y家里有一个大森林,里面有n棵树,编号从1到n。一开始这些树都只是树苗,只有一个节点,标号为1。这些树都有一个特殊的节点,我们称之为生长节点,这些节点有生长出子节点的能力。
小Y掌握了一种魔法,能让第l棵树到第r棵树的生长节点长出一个子节点。同时她还能修改第l棵树到第r棵树的生长节点。她告诉了你她使用魔法的记录,你能不能管理她家的森林,并且回答她的询问呢?
输入输出格式
输入格式:
第一行包含 2 个正整数n,m,共有n棵树和m个操作。
接下来 m 行,每行包含若干非负整数表示一个操作,操作格式为:
0 l r表示将第l棵树到第r棵树的生长节点下面长出一个子节点,子节点的标号为上一个0号操作叶子标号加1(例如,第一个
0
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 存在。
输出格式:
输出包括若干行,按顺序对于每个小Y的询问输出答案
输入输出样例
输入样例#1:
5 5
0 1 5
1 2 4 2
0 1 4
2 1 1 3
2 2 1 3
输出样例#1:
1
2
分析:
我们先排掉不合法的操作,操作0的编号是指上一次操作0编号+1,而不是当前子树编号加1。所以我们可以记录每个编号的区间,然后区间求交。
排掉所有不合法操作后,我们发现一个优美的性质,因为只能加点,又没有修改边权,所以对于一棵树,所有操作完成后再询问和中间询问其实是一样的。我们把操作离线,把
1
1
到的树依次搞出来,再完成当前树的询问。
因为是区间换根以及区间加点,对于区间
[l,r]
[
l
,
r
]
,可以在
l
l
处加入,再到处删掉就可以了。
我们考虑把所有1操作的点
x
x
都建一个虚点(包括一开始的
1
1
节点),然后把他们连成一条链,这条链上的权值为。我们把每个加点操作都挂在最近的1操作的虚点上面。我们可以这么看,这个点实际上加在树上的位置,就是这个点沿着虚链向上走到的节点,显然一开始走到的都是
1
1
节点,说明这个点的父亲是。如果我们进行了换根操作,假设换了的根为
i′
i
′
,那么把
i′
i
′
向他前一个1操作的点的虚点的边断掉,连接
i
i
和就可以了。
具体来说这么做:
1.
1.
把操作离线,修改操作变为一个加入一个一个删除,查询不变。
2.
2.
每个一操作对应的
x
x
建一个虚点,并向前一个操作的点连一条权值为
0
0
的边,的虚点向
1
1
连一条权值为的边。
3.
3.
对与0操作的加入,直接向他最近的1操作的虚点连权值为1的边,删除操作相反。
4.
4.
对于1操作的加入,断开前一个1操作的虚点的边,连一条这个点的虚点到实点的边,权值为0。
5.
5.
查询直接树上查询两个实点的距离。可以发现,这棵树就是我们建出来的的树。
代码:
// luogu-judger-enable-o2
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
const int maxn=3e5+7;
const int maxq=4e5+7;
using namespace std;
struct rec{
int op,pos,num,u,v,ans;
}q[maxq];
struct tree{
int fa,l,r,data,sum;
bool rev;
}t[maxn];
int L[maxn],R[maxn];
int n,x,op,l,r,m,num1,cnt,num2;
bool cmp1(rec x,rec y)
{
if (x.pos==y.pos) return x.op<y.op;
return x.pos<y.pos;
}
bool cmp2(rec x,rec y)
{
return x.num<y.num;
}
void updata(int x)
{
int l=t[x].l,r=t[x].r;
t[x].sum=t[l].sum+t[r].sum+t[x].data;
}
bool isroot(int x)
{
return (x!=t[t[x].fa].l) && (x!=t[t[x].fa].r);
}
void remove(int x)
{
if (!isroot(x)) remove(t[x].fa);
if (t[x].rev)
{
t[x].rev^=1;
swap(t[x].l,t[x].r);
if (t[x].l) t[t[x].l].rev^=1;
if (t[x].r) t[t[x].r].rev^=1;
}
}
void rttr(int x)
{
int y=t[x].l;
t[x].l=t[y].r;
if (t[y].r) t[t[y].r].fa=x;
if (x==t[t[x].fa].l) t[t[x].fa].l=y;
else if (x==t[t[x].fa].r) t[t[x].fa].r=y;
t[y].fa=t[x].fa;
t[x].fa=y;
t[y].r=x;
updata(x); updata(y);
}
void rttl(int x)
{
int y=t[x].r;
t[x].r=t[y].l;
if (t[y].l) t[t[y].l].fa=x;
if (x==t[t[x].fa].l) t[t[x].fa].l=y;
else if (x==t[t[x].fa].r) t[t[x].fa].r=y;
t[y].fa=t[x].fa;
t[x].fa=y;
t[y].l=x;
updata(x); updata(y);
}
void splay(int x)
{
remove(x);
while (!isroot(x))
{
int p=t[x].fa,g=t[p].fa;
if (isroot(p))
{
if (x==t[p].l) rttr(p);
else rttl(p);
}
else
{
if (x==t[p].l)
{
if (p==t[g].l) rttr(p),rttr(g);
else rttr(p),rttl(g);
}
else
{
if (p==t[g].l) rttl(p),rttr(g);
else rttl(p),rttl(g);
}
}
}
}
void access(int x)
{
int y=0;
while (x)
{
splay(x);
t[x].r=y;
updata(x);
y=x;x=t[x].fa;
}
}
void makeroot(int x)
{
access(x);
splay(x);
t[x].rev^=1;
}
void link(int x,int y)
{
makeroot(x);
access(y);
splay(y);
t[x].fa=y;
}
void cut(int x,int y)
{
makeroot(x);
access(y);
splay(y);
t[x].fa=0; t[y].l=0;
updata(y);
}
int main()
{
scanf("%d%d",&n,&m);
L[1]=1,R[1]=n;
num1=1;
for (int i=1;i<=m;i++)
{
scanf("%d",&op);
if (op==0)
{
scanf("%d%d",&l,&r);
num1++;
L[num1]=l; R[num1]=r;
q[++cnt]=(rec){1,l,i,num1,0,0};
q[++cnt]=(rec){2,r+1,i,num1,0,0};
}
if (op==1)
{
scanf("%d%d%d",&l,&r,&x);
l=max(l,L[x]);
r=min(r,R[x]);
if (l>r) continue;
if (x>num1) continue;
q[++cnt]=(rec){3,l,i,x,0,0};
q[++cnt]=(rec){4,r+1,i,x,0,0};
}
if (op==2)
{
scanf("%d%d%d",&x,&l,&r);
q[++cnt]=(rec){5,x,i,l,r,0};
}
}
num2=num1+1;
for (int i=1;i<=cnt;i++)
{
if (q[i].op==1) q[i].v=num2;
if (q[i].op==2) q[i].v=num2;
if (q[i].op==3) q[i].v=++num2;
if (q[i].op==4) q[i].v=num2;
}
sort(q+1,q+cnt+1,cmp1);
link(1,num1+1);
for (int i=num1+2;i<=num2;i++) link(i-1,i);
for(int i=1;i<=num1;i++) t[num2+i].data=1;
for (int i=1;i<=cnt;i++)
{
if (q[i].op==1)
{
link(q[i].u,q[i].u+num2);
link(q[i].u+num2,q[i].v);
}
if (q[i].op==2)
{
cut(q[i].u,q[i].u+num2);
cut(q[i].u+num2,q[i].v);
}
if (q[i].op==3)
{
cut(q[i].v-1,q[i].v);
link(q[i].u,q[i].v);
}
if (q[i].op==4)
{
cut(q[i].u,q[i].v);
link(q[i].v-1,q[i].v);
}
if (q[i].op==5)
{
makeroot(q[i].u);
access(q[i].v);
splay(q[i].v);
q[i].ans=t[q[i].v].sum;
}
}
sort(q+1,q+cnt+1,cmp2);
for (int i=1;i<=cnt;i++)
{
if (q[i].op==5) printf("%d\n",q[i].ans);
}
}