bzoj-2002 Bounce 弹飞绵羊

本文介绍了一道关于弹力装置的算法问题,并使用LCT树和Splay树来解决该问题。通过维护树的结构,可以有效地查询每个绵羊被弹射的次数,并支持对弹力装置的动态调整。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:

直线上有一排n个弹力装置,每个弹力装置会将绵羊弹到下ki个弹力装置处;

如果没有了则绵羊被弹飞。。

问每个绵羊被弹了几次弹飞;

可能会修改弹力装置的k值;

n<=200000,m<=100000;


题解:

裸的LCT吧;

所以下面的启发式合并Splay是啥鬼;

有人说这题边有向,和无向边不一样;

然而有个卵区别,把终点作为根不就有向了吗!

反正切了上一题这一题也不难吧;

维护个size之后,把终点作为根再access(x);

这时的Splay就是x弹飞的路线啦,Splay(x)之后x的左子树大小就是答案咯;

其实也是整棵Splay的大小-1,因为x的重儿子在access时砍掉了嘛;

LCT是啥啥的还是戳上一篇。。


代码:


#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 210000
#define which(x) (ch[fa[x]][1]==x)
using namespace std;
int fa[N],ch[N][2],size[N],to[N],root;
bool rt[N],cov[N];
void Pushup(int x)
{
	size[x]=size[ch[x][0]]+size[ch[x][1]]+1;
}
void reverse(int x)
{
	swap(ch[x][0],ch[x][1]);
	cov[x]^=1;
}
void Pushdown(int x)
{
	if(cov[x])
	{
		reverse(ch[x][0]);
		reverse(ch[x][1]);
		cov[x]=0;
	}
}
void down(int x)
{
	if(!rt[x])	down(fa[x]);
	Pushdown(x);
}
void Rotate(int x)
{
	int f=fa[x];
	bool k=which(x);
	if(rt[f])	rt[f]^=rt[x]^=1;
	else		ch[fa[f]][which(f)]=x;
	ch[f][k]=ch[x][!k];
	ch[x][!k]=f;
	fa[ch[f][k]]=f;
	fa[x]=fa[f];
	fa[f]=x;
	size[x]=size[f];
	Pushup(f);
}
void Splay(int x)
{
	down(x);
	while(!rt[x])
	{
		int f=fa[x];
		if(rt[f])
		{
			Rotate(x);
			return ;
		}
		if(which(x)^which(f))
			Rotate(x);
		else
			Rotate(f);
		Rotate(x);
	}
}
void access(int x)
{
	int y=0;
	while(x)
	{
		Splay(x);
		rt[ch[x][1]]=1;
		rt[y]=0;
		ch[x][1]=y;
		Pushup(x);
		y=x,x=fa[x];
	}
}
void Mtr(int x)
{
	access(x);
	Splay(x);
	reverse(x);
}
void Link(int x,int y)
{
	Mtr(x);
	fa[x]=y;
}
void Cut(int x,int y)
{
	Mtr(x);
	access(y);
	Splay(x);
	ch[x][1]=0,fa[y]=0;
	rt[y]=1;
	Pushup(x);
}
int Query(int x)
{
	Mtr(root);
	access(x);
	Splay(x);
	return size[ch[x][0]];
}
int main()
{
	int n,m,i,j,k,x,y,op;
	scanf("%d",&n);
	root=n+1;
	for(i=1;i<=n+1;i++)
		rt[i]=1,size[i]=1;
	for(i=1;i<=n;i++)
	{
		scanf("%d",&k);
		to[i]=min(i+k,root);
		Link(i,to[i]);
	}
	scanf("%d",&m);
	for(i=1;i<=m;i++)
	{
		scanf("%d",&op);
		if(op==1)
		{
			scanf("%d",&x),x++;
			printf("%d\n",Query(x));
		}
		else
		{
			scanf("%d%d",&x,&k),x++;
			Cut(x,to[x]);
			to[x]=min(x+k,root);
			Link(x,to[x]);
		}
	}
	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值