【BZOJ 2002】 [Hnoi2010]Bounce 弹飞绵羊 LCT

感觉LCT实在是太好玩了qwq。。。。。
这里我们首先思考没有改变操作该怎么做?可以把每一个节点和他将要到达的点(作为父亲节点)连边(建立一个虚拟的根节点表示弹飞绵羊),然后我们就得到了一棵树,每一个点需要弹射几次能够被弹飞就是他的深度-1咯(根节点深度为1)。但是如果有修改操作的话,说明树是动态的,LCT解决动态树,不会LCT的去切BZOJ 2049

说一下这里的link和cut操作,首先cut操作,首先我们access(a)这样由于i和fa[i]是相邻的而且fa[a]的深度更小,所以splay(a)以后,fa[a]就是a的左儿子了,切断以后更新一下就好了。

然后是link操作。现在link(a,b) (b是新的a的父亲节点),所以总的根节点一定是在b所在的树内对吧,也就是说是将a连入b,而在a这棵辅助树里,a的深度一定是最小的对吧(刚刚才cut出来的),所以他就是暂时原树的根节点咯,所以直接splay以后fa[a]=b就好了。

#include
#include
#include
#define maxn 200020
#define ls(u) ch[u][0]
#define rs(u) ch[u][1]
using namespace std;
int n,m,k[maxn],fa[maxn],ch[maxn][2],size[maxn],rt;
inline int Q(int u){return u==rs(fa[u]);}
inline bool isrt(int u){return !fa[u]||(u!=ls(fa[u])&&u!=rs(fa[u]));}
inline void push_up(int u){size[u]=size[ls(u)]+size[rs(u)]+1;}
void rotate(int u){
	if(!u)return;
	int f=fa[u],ff=fa[f],d=!Q(u);
	if(!isrt(f))ch[ff][Q(f)]=u;fa[u]=ff;
	if(ch[u][d])fa[ch[u][d]]=f;ch[f][!d]=ch[u][d];
	ch[u][d]=f;fa[f]=u;
	push_up(f),push_up(u);
} 
void splay(int u){
	if(!u)return;
	while(!isrt(u)){
		int f=fa[u];
		if(!isrt(f)){
			if(Q(u)==Q(f))rotate(f);
			else rotate(u);
		}
		rotate(u);
	}
}
void access(int u){
	for(int x=0;u;u=fa[x=u]){
		splay(u);rs(u)=x;
		push_up(u);
	}
}
void cut(int a){
	access(a),splay(a);
	fa[ls(a)]=0,ls(a)=0;
	push_up(a);
}
void link(int a,int b){
	access(a),splay(a);
	fa[a]=b;
}

int main(){
	scanf("%d",&n);rt=n+1;
	for(int i=1;i<=n;i++){
		scanf("%d",k+i);
		if(i+k[i]<=n)fa[i]=i+k[i];
		else fa[i]=rt;
	}
	scanf("%d",&m);
	int pos,a,b;
	while(m--){
		scanf("%d",&pos);
		if(pos==1){
			scanf("%d",&a);a++;
			access(a),splay(a);
			printf("%d\n",size[ls(a)]);
		}else {
			scanf("%d%d",&a,&b);a++;
			a+b>n?b=rt:b=a+b;
			cut(a),link(a,b);
		}
	}
	return 0;
}


你可能感兴趣的:(LCT,bzoj,ac之路)