[HNOI2010]BOUNCE 弹飞绵羊

Posted Mrsrz

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[HNOI2010]BOUNCE 弹飞绵羊相关的知识,希望对你有一定的参考价值。

题目:BZOJ2002、洛谷P3203、codevs2333。

题目大意:有n个装置,编号0~n-1。每个装置有一个弹跳系数,第i个装置可以把羊弹到第$i+k_i$个装置上,然后继续弹。如果这个位置没有装置,则停止弹。

现在有m个操作,可以问你一只绵羊在某点开始弹,弹多少次才能结束,也可以修改一个装置的弹跳系数。

解题思路:分块。

一般都是每块分成$\sqrt{n}$个(最后一个可能不同),分成$\sqrt{n}$或$\sqrt{n}+1$块。

设jp[i]表示从这个点跳到另一个块的某个点要多少下,nxt[i]表示从i开始跳,跳到另一个块后,跳到的节点的编号。则可以发现,更新一个弹跳系数,只会变化该块内的答案(一个点可能跳到相同块内一个点)。

然后记录下每个块的起点,更新答案时一个一个更新即可(注意倒着更新)。

输出弹跳次数则更加简单,每次累计jp[i],然后令i=nxt[i]即可。

更新答案最多涉及一整个块,输出弹跳次数最多每个块有一个节点被访问,所以一次处理的时间复杂度为$O(\sqrt{n})$。

则总时间复杂度为$O(m\sqrt{n})$。

当然您也可以用LCT秒了这道题。

C++ Code:

#include<cstdio>
#include<cctype>
#include<cstring>
#include<cmath>
#define N 200020
int n,size,jp[N],nxt[N],k[N],l[505],inbk[N],blocks;
inline int readint(){
	char c=getchar();
	for(;!isdigit(c);c=getchar());
	int d=0;
	for(;isdigit(c);c=getchar())
	d=(d<<3)+(d<<1)+(c^‘0‘);
	return d;
}
int ask(int s){
	int ans=0;
	while(s!=-1){
		ans+=jp[s];
		s=nxt[s];
	}
	return ans;
}
int main(){
	n=readint();
	size=(int)(sqrt(n)+0.00000001);
	blocks=0;
	l[0]=0;
	for(int i=0;i<n;++i){
		k[i]=readint();
		inbk[i]=blocks;
		if(i%size==size-1)l[++blocks]=i+1;
	}
	if(n%size)++blocks;
	memset(nxt,-1,sizeof nxt);
	for(int i=n-1;i>=0;--i){
		if(i+k[i]>=n)jp[i]=1;else
		if(inbk[i]==inbk[i+k[i]])
		jp[i]=jp[i+k[i]]+1,nxt[i]=nxt[i+k[i]];else
		jp[i]=1,nxt[i]=i+k[i];
	}
	for(int m=readint();m--;){
		int f=readint();
		if(f==1)printf("%d\n",ask(readint()));else{
			int x=readint();
			k[x]=readint();
			for(int i=x;i>=l[inbk[x]];--i)
			if(inbk[i]==inbk[i+k[i]])jp[i]=jp[i+k[i]]+1,nxt[i]=nxt[i+k[i]];else
			jp[i]=1,nxt[i]=i+k[i];
		}
	}
	return 0;
}

 

以上是关于[HNOI2010]BOUNCE 弹飞绵羊的主要内容,如果未能解决你的问题,请参考以下文章

bzoj 2002: [Hnoi2010]Bounce 弹飞绵羊

BZOJ2002: [Hnoi2010]Bounce 弹飞绵羊

[BZOJ2002][Hnoi2010]Bounce 弹飞绵羊

bzoj2002HNOI2010Bounce 弹飞绵羊

[bzoj2002][Hnoi2010]Bounce弹飞绵羊_LCT

[HNOI 2010]Bounce 弹飞绵羊