bzoj2002: [Hnoi2010]Bounce 弹飞绵羊

链接

  http://www.lydsy.com/JudgeOnline/problem.php?id=2002

题解

  上次使用分块A掉的...这次来一发LCT题解

  LCT最大的注意事项:rev标记一定要及时下放,否则新的树link上时就会被错误地纳入reverse范围内。

  别的....倒没啥吧,这道题比较裸,其实rev标记都是多余的,因为你把整棵树摘下来再连到另一个地方,你access的总是最头上的节点,根本没有需要翻转的链。

  答案就是access之后直接输出左子树的大小。

  我调了5节课哎!时间悄然流走.....

  1A.

代码

//动态树 
#include <cstdio>
#include <algorithm>
#define maxn 300000
using namespace std;
int N, M, a[maxn], k[maxn];
struct node
{
	int rev, size;
	node *ch[2], *f;
}nd[maxn], *s[maxn];
inline int getwh(node *x)
{if(x->f==0)return -1;if(x->f->ch[0]==x)return 0;if(x->f->ch[1]==x)return 1;return -1;}
inline bool isroot(node *x){return getwh(x)==-1;}
inline void join(node *y, node *x, int wh){if(y)y->ch[wh]=x;if(x)x->f=y;}
inline void tag(node *x){if(x)x->rev^=1;}
inline void pushdown(node *x)
{
	if(x->rev)
	{
		tag(x->ch[0]),tag(x->ch[1]);
		swap(x->ch[0],x->ch[1]);
		x->rev=0;
	}
}
inline void pushup(node *x)
{
	int &t=x->size=1;
	if(x->ch[0])t+=x->ch[0]->size;
	if(x->ch[1])t+=x->ch[1]->size;
}
inline void rotate(node *x)
{
	node *y=x->f, *z=y->f;
	int c=getwh(x);
	if(!isroot(y))join(z,x,getwh(y));
	else x->f=y->f;
	join(y,x->ch[!c],c);
	join(x,y,!c);
	pushup(y),pushup(x);
}
inline void splay(node *x)
{
	node *y;
	int top=0;
	for(y=x;!isroot(y);y=y->f)s[++top]=y;s[++top]=y;
	for(;top;top--)pushdown(s[top]);
	while(!isroot(x))
	{
		y=x->f;
		if(isroot(y)){rotate(x);break;}
		if(getwh(x)^getwh(y))rotate(x);
		else rotate(y);
		rotate(x);
	}
}
inline void access(node *x)
{
	node *t=0;
	while(x)
	{
		splay(x);
		x->ch[1]=t;
		t=x,x=x->f;
	}
}
inline void link(node *x, node *y){access(x);splay(x);tag(x);x->f=y;}
inline void cut(node *x){access(x);splay(x);if(x->ch[0])x->ch[0]=x->ch[0]->f=0;}
int main()
{
	int i, type, a, b;
	scanf("%d",&N);
	for(i=1;i<=N;i++)nd[i].size=1;
	for(i=1;i<=N;i++)scanf("%d",k+i),nd[i].f=i+k[i]<=N?&nd[i+k[i]]:0;
	scanf("%d",&M);
	for(i=1;i<=M;i++)
	{
		scanf("%d%d",&type,&a);a++;
		if(type==1)
		{
			access(nd+a);
			splay(nd+a);
			if(nd[a].ch[0])printf("%d\n",nd[a].ch[0]->size+1);
			else printf("1\n");
		}
		if(type==2)
		{
			scanf("%d",&b);
			if(a+k[a]<=N)cut(nd+a);
			if(a+b<=N)link(nd+a,nd+a+b);
			k[a]=b;
		}
	}
	return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值