[HNOI2010]弹飞绵羊

考虑这是一个\(LCT\)模板题。
感觉得多做一些题来熟悉\(LCT\)的操作。
这个题考虑对每个点向他往后跳的终点,如果会出界就不连边。
然后考虑\(LCT\)维护,也就是查询该点到原树根的距离。
那就\(access\),\(splay\),然后查询\(x\)的子树大小就行了。
断边的话,因为保证了树结构而且断的边都是子节点向父节点断的边,所以考虑\(access\),\(splay\)完,则\(x\)成为树根,那么他的父节点必为他的左儿子,直接双向断就行了。

[HNOI2010]弹飞绵羊
#include<iostream>
#include<cstdio>
#define ll long long
#define N 200009

ll f[N],c[N][2],s[N];//父亲,儿子,权值

#define l(x) c[x][0]
#define r(x) c[x][1]
#define f(x) f[x]

inline bool nroot(int x){return l(f(x)) == x || r(f(x)) == x;}//一个点是否不是splay的根

inline void up(int x){s[x] = s[l(x)] + s[r(x)] + 1;}

inline void rotate(int x){
//	std::cout<<x<<std::endl;	
	int y = f(x),z = f(y),k = (r(y) == x),w = c[x][!k];
	if(nroot(y))
	c[z][r(z) == y] = x;c[x][!k] = y;c[y][k] = w;
	if(w)f(w) = y;f(y) = x;f(x) = z;
	up(y);up(x);
} 

inline void splay(int x){
//	std::cout<<x<<std::endl;
	int y,z;
	while(nroot(x)){
		y = f(x);z = f(y);
		if(nroot(y))
		rotate((l(y) == x) ^ (l(z) == y)?x:y);
		rotate(x);
	}
	up(x);
}

inline void access(int x){
	for(int y = 0;x;x = f[y = x])
	splay(x),c[x][1] = y,up(x);
}

ll n,m;

int main(){
	scanf("%lld",&n);
	for(int i = 1;i <= n;++i){
		s[i] = 1;
		ll k;
		scanf("%lld",&k);
		if(i + k <= n)
		f[i] = i + k;
	}
	scanf("%lld",&m);
	while(m -- ){
		ll opt,x,y;
		scanf("%lld",&opt);
		if(opt & 1){
			scanf("%lld",&x);
			++x;
			access(x),splay(x);
			std::cout<<s[x]<<std::endl;
		}else{
			scanf("%lld%lld",&x,&y);
			++x; 
			access(x),splay(x);
			l(x) = f(l(x)) = 0;
			if(x + y <= n)
			f(x) = x + y;
			up(x);
		}
	}
}
posted @ 2021-05-05 22:03  fhq_treap  阅读(50)  评论(0编辑  收藏  举报