题目描述
小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
splay维护括号序做法
我们可以想到离线去做
把0和1操作分别挂左右端点,进入时插入离开时删除。
从左到右,我们需要维护树的形态信息,同时需要支持插入、删除一个0或1操作。
容易想到用括号序把树表示出来。
插入0操作的话,这个操作对应点的父亲是在其时间之前最晚一个存在的1操作的对应点。
删除0操作也一样。
而插入1操作会使得一连串的0操作更换了自己的父亲。
为了能够准确分离出这段,我们还得规定同一个点的子树严格按时间顺序。
插入时在splay上二分一下。
询问的话,括号序是可以求两点的距离的。
然后因为1操作可能不合法,我们其实可以把1操作的对应区间根据其对应0操作的对应区间进行更改,就不用考虑1操作不合法了。
这样写起来比较难
LCT大法
我们对于每一个1操作建一个虚点。
为了区分真实存在的点与辅助用的虚点,我们引入点权,虚点为0实点为1。
每一个0操作对应点都挂在其前面时间最晚的1操作对应虚点下。
当一个1操作当前不生效时,其也挂在其前面时间最晚的1操作对应虚点下,但若其生效了,我们把它挂在其对应点下。
这样做显然保证了正确性。
那么随手打个LCT就能做了。
#include<cstdio>
#include<algorithm>
#include<set>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
const int maxn=200000+10;
struct dong{
int t,ca,x,y,z,ans;
friend bool operator <(dong a,dong b){
return a.t<b.t;
}
} ask[maxn],zlt;
int h[maxn],go[maxn*2],next[maxn*2],dis[maxn*2],b[maxn],gs[maxn],lt[maxn],rt[maxn];
int h2[maxn],g2[maxn],n2[maxn];
int h3[maxn],g3[maxn],n3[maxn],now[maxn];
int father[maxn*2],tree[maxn*2][2],key[maxn*2],sum[maxn*2],pp[maxn*2],sta[maxn*2];
bool bz[maxn*2];
int i,j,k,l,r,t,x,y,n,m,tot,top,cnt,num,ans;
int read(){
int x=0,f=1;
char ch=getchar();
while (ch<'0'||ch>'9'){
if (ch=='-') f=-1;
ch=getchar();
}
while (ch>='0'&&ch<='9'){
x=x*10+ch-'0';
ch=getchar();
}
return x*f;
}
void add(int x,int y,int z){
go[++cnt]=y;
dis[cnt]=z;
next[cnt]=h[x];
h[x]=cnt;
}
int pd(int x){
return tree[father[x]][1]==x;
}
void update(int x){
sum[x]=sum[tree[x][0]]+sum[tree[x][1]]+key[x];
}
void rotate(int x){
int y=father[x],z=pd(x);
father[x]=father[y];
if (father[y]) tree[father[y]][pd(y)]=x;
tree[y][z]=tree[x][1-z];
if (tree[x][1-z]) father[tree[x][1-z]]=y;
tree[x][1-z]=y;
father[y]=x;
update(y);
update(x);
if (pp[y]) pp[x]=pp[y],pp[y]=0;
}
void clear(int x){
if (bz[x]){
if (tree[x][0]) bz[tree[x][0]]^=1;
if (tree[x][1]) bz[tree[x][1]]^=1;
swap(tree[x][0],tree[x][1]);
bz[x]=0;
}
}
void remove(int x,int y){
top=0;
while (x!=y){
sta[++top]=x;
x=father[x];
}
while (top){
clear(sta[top]);
top--;
}
}
void splay(int x,int y){
remove(x,y);
while (father[x]!=y){
if (father[father[x]]!=y)
if (pd(x)==pd(father[x])) rotate(father[x]);else rotate(x);
rotate(x);
}
}
int access(int x){
int y,z,p=x;
splay(x,0);
z=tree[x][1];
tree[x][1]=0;
if (z){
father[z]=0;
pp[z]=x;
}
update(x);
while (pp[x]){
p=y=pp[x];
splay(y,0);
z=tree[y][1];
if (z){
father[z]=0;
pp[z]=y;
}
tree[y][1]=x;
father[x]=y;
pp[x]=0;
update(y);
splay(x,0);
}
return p;
}
void makeroot(int x){
access(x);
splay(x,0);
bz[x]^=1;
}
void link(int x,int y){
makeroot(y);
splay(y,0);
pp[y]=x;
}
void cut(int x){
makeroot(1);
access(x);
splay(x,0);
int y=tree[x][0];
tree[x][0]=0;
if (y){
father[y]=0;
pp[y]=0;
}
update(x);
}
void insert1(int x){
int j=b[ask[x].y-1],k=b[ask[x].y];
cut(k);
link(k,ask[x].x);
}
void delete1(int x){
int j=b[ask[x].y-1],k=b[ask[x].y];
cut(k);
link(k,j);
}
void insert0(int x){
int j=gs[ask[x].x];
key[ask[x].x]=1;
update(ask[x].x);
link(ask[x].x,b[j]);
}
void delete0(int x){
int j=gs[ask[x].x];
cut(ask[x].x);
}
void write(int x){
if (!x){
putchar('0');
putchar('\n');
return;
}
top=0;
while (x){
sta[++top]=x%10;
x/=10;
}
while (top){
putchar('0'+sta[top]);
top--;
}
putchar('\n');
}
int main(){
//freopen("ex_forest2.in","r",stdin);freopen("data.out","w",stdout);
n=read();m=read();
tot=1;top=1;
lt[1]=1;rt[1]=n;
fo(i,1,m){
ask[i].t=i;
ask[i].ca=read();
if (ask[i].ca==0){
l=read();r=read();
add(l,i,1);add(r+1,i,-1);
ask[i].x=++tot;
lt[tot]=l;rt[tot]=r;
gs[tot]=top;
}
else if (ask[i].ca==1){
l=read();r=read();
ask[i].x=read();
l=max(l,lt[ask[i].x]);r=min(r,rt[ask[i].x]);
if (l<=r){
add(l,i,1);add(r+1,i,-1);
ask[i].y=++top;
}
}
else{
ask[i].z=l=read();
add(l,i,1);
ask[i].x=read();ask[i].y=read();
}
}
zlt.t=0;
zlt.ca=1;
zlt.x=1;
zlt.y=1;
ask[0]=zlt;
fo(i,1,top) b[i]=++tot;
key[1]=1;
update(1);
pp[b[1]]=1;
fo(i,2,top) pp[b[i]]=b[i-1];
cnt=0;
fo(i,1,n){
if (i==441){
t=t;
}
t=h[i];
while (t){
if (ask[go[t]].ca==0){
if (dis[t]==1) insert0(go[t]);else delete0(go[t]);
}
else if (ask[go[t]].ca==1){
if (dis[t]==1) insert1(go[t]);else delete1(go[t]);
}
t=next[t];
}
t=h[i];
while (t){
if (ask[go[t]].ca==2){
x=ask[go[t]].x;y=ask[go[t]].y;
if (x==y){
ask[go[t]].ans=0;
t=next[t];
continue;
}
makeroot(1);
access(x);
splay(x,0);
ans=sum[x];
x=access(y);
splay(y,0);
ans+=sum[y];
//x=find(x);
access(x);
splay(x,0);
ans-=sum[x]*2;
ask[go[t]].ans=ans;
}
t=next[t];
}
}
/*cnt=0;top=0;
fo(i,1,m)
if (ask[i].ca==2) now[++top]=i;
fo(i,1,top){
if (ask[now[i-1]].ans==3&&ask[now[i]].ans==4&&ask[now[i+1]].ans==3){
cnt++;
if (cnt==1) break;
}
}
t=t;*/
fo(i,1,m)
if (ask[i].ca==2) write(ask[i].ans);
}