bzoj 4573
LCT神题...
首先暴力的做法就是每次在一个区间上link,然后暴力查询,时间复杂度$O(爆炸)$
但是我们可以发现的是,每棵树之间互不影响!
因此我们可以考虑离线之后分别统计每棵树
但是这样做其实并没有优化时空啊!
但是等等,我们在考虑一下:我们的操作都是区间操作,也就是说我们可以用一个类似差分的思想,一棵树一棵树地处理,处理到区间端点的时候修改一些东西就行了!
那么更换生长节点怎么办?
我们建立一个虚点表示生长节点,然后把新产生的点都连到这个点上就行
于是思路就顺理成章了:
对于新建节点的操作,我们新生成一个点权为1的节点,记录下这个节点生效的区间,然后把他挂到现在的生长节点上
对于更换生长节点的操作,我们新建一个点权为0的节点,将读入的区间与这个点生效的区间取交集作为有效的区间,在这个区间左端点把他挂在要求的实际节点上,右端点挂回虚节点链上
对于查询操作,直接用LCA查询即可,求LCA的方法见代码中access函数
然后离线所有操作,先按树的标号排序(注意新建节点的操作都认为是第一棵树上的,这样后面才能产生足够的节点去移动),先进行修改再进行查询(可以在排序时通过正负把查询排到后面)
这样问题就解决了
#include <cstdio> #include <cmath> #include <cstring> #include <cstdlib> #include <iostream> #include <algorithm> #include <queue> #include <stack> using namespace std; struct Ques { int pos,typ; int x,y; Ques (){} Ques (int a,int b,int c,int d):pos(a),typ(b),x(c),y(d){} friend bool operator < (Ques a,Ques b) { return a.pos==b.pos?a.typ<b.typ:a.pos<b.pos; } }q[400005]; int ch[400005][2]; int siz[400005],f[400005],val[400005]; int ret[400005]; int fl[400005],fr[400005]; int lq[400005],rq[400005]; int n,m,Q; int tot=0,cnt=0,ttop=0; void update(int x) { siz[x]=siz[ch[x][0]]+siz[ch[x][1]]+val[x]; } bool be_root(int x) { if(ch[f[x]][0]==x||ch[f[x]][1]==x)return 0; return 1; } void rotate(int x) { int y=f[x],z=f[y],k=(ch[y][1]==x); if(!be_root(y))ch[z][ch[z][1]==y]=x; f[x]=z; ch[y][k]=ch[x][!k],f[ch[x][!k]]=y; ch[x][!k]=y,f[y]=x; update(y),update(x); } void splay(int x) { while(!be_root(x)) { int y=f[x],z=f[y]; if(!be_root(y)) { if((ch[z][1]==y)^(ch[y][1]==x))rotate(x); else rotate(y); } rotate(x); } //update(x); } int access(int x) { int y=0; while(x) { splay(x); ch[x][1]=y; update(x); y=x,x=f[x]; } return y; } void link(int x,int y) { splay(x),f[x]=y; } void cut(int x) { access(x),splay(x),f[ch[x][0]]=0,ch[x][0]=0,update(x); } void new_node(int x) { tot++,val[tot]=siz[tot]=x; } int query(int x,int y) { int ret=0,fa; access(x),splay(x),ret+=siz[x]; fa=access(y),splay(y),ret+=siz[y]; access(fa),splay(fa),ret-=2*siz[fa]; return ret; } inline int read() { int f=1,x=0;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; } int main() { n=read(),m=read(); new_node(1),new_node(0); cnt=1,lq[1]=fr[1]=1,rq[1]=n; int las=2;link(2,1); for(int i=1;i<=m;i++) { int t=read(); if(!t) { int l=read(),r=read(); new_node(1); lq[++cnt]=l,rq[cnt]=r,fr[cnt]=tot; q[++ttop]=Ques(1,i-m,tot,las); }else if(t==1) { int l=read(),r=read(),x=read(); l=max(l,lq[x]),r=min(r,rq[x]); if(l<=r) { new_node(0),link(tot,las); q[++ttop]=Ques(l,i-m,tot,fr[x]); q[++ttop]=Ques(r+1,i-m,tot,las); las=tot; } }else { int x=read(),st=read(),ed=read(); q[++ttop]=Ques(x,++Q,fr[st],fr[ed]); } } sort(q+1,q+ttop+1); int last=1; for(int i=1;i<=n;i++) { while(q[last].pos==i&&last<=ttop) { if(q[last].typ<=0)cut(q[last].x),link(q[last].x,q[last].y); else ret[q[last].typ]=query(q[last].x,q[last].y); last++; } } for(int i=1;i<=Q;i++)printf("%d\n",ret[i]); return 0; }