#195. 【ZJOI2016】大森林
小Y家里有一个大森林,里面有 n n 棵树,编号从 到 n n 。一开始这些树都只是树苗,只有一个节点,标号为 。这些树都有一个特殊的节点,我们称之为生长节点,这些节点有生长出子节点的能力。
小Y掌握了一种魔法,能让第 l l 棵树到第 棵树的生长节点长出一个子节点。同时她还能修改第 l l 棵树到第 棵树的生长节点。
她告诉了你她使用魔法的记录,你能不能管理她家的森林,并且回答她的询问呢?
输入格式
第一行包含 2 个正整数 n,m n , m ,共有 n n 棵树和 个操作。
接下来 m m 行,每行包含若干非负整数表示一个操作,操作格式为:
-
l
l
表示将第
l
l
棵树到第 棵树的生长节点下面长出一个子节点,子节点的标号为上一个
0
0
号操作叶子标号加 (例如,第一个
0
0
号操作产生的子节点标号为 ),
l
l
到 之间的树长出的节点标号都相同。保证
1≤l≤r≤n
1
≤
l
≤
r
≤
n
。
- 1 1 r r 表示将第 l l 棵树到第 棵树的生长节点改到标号为 x x 的节点。对于区间内的每棵树,如果标号 的点不在其中,那么这个操作对该树不产生影响。保证 1≤l≤r≤n 1 ≤ l ≤ r ≤ n , x x 不超过当前所有树中节点最大的标号。
- x x v v 询问第 棵树中节点 u u 到节点 的距离,也就是在第 x x 棵树中从节点 和节点 v v 的最短路上边的数量。保证 ,这棵树中节点 u u 和节点 存在。
输出格式
输出包括若干行,按顺序对于每个小Y的询问输出答案。
样例一
input
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 约定 1 ≤103 ≤ 10 3 ≤103 ≤ 10 3 2 ≤105 ≤ 10 5 ≤2×105 ≤ 2 × 10 5 保证每次 0 0 和 操作修改的是 1 1 到 所有的树 3 4 保证每次 0 0 操作生长节点都是这些树中编号最大的节点 5 6 7 8 9 10 时间限制:
空间限制: 256MB 256 MB
思路&&分析:
对于每一个1操作,我们可以建一个虚点,然后对于每个0操作都将这些点挂在[在这个操作前面最迟的1操作上建的虚点]的下面。于是我们可以将操作离线,然后从左到右把所有树都扫一遍就行了,这样一来我们打一颗LCT就完事了。
Code
#pragma GCC optimize(3) #include<bits/stdc++.h> 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);} /*================Header Template==============*/ 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?op<rhs.op:pos<rhs.pos; } }a[maxn<<2]; 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; }