bzoj 2002 [Hnoi2010]Bounce 弹飞绵羊

Description

某天,Lostmonkey发明了一种超级弹力装置,为了在他的绵羊朋友面前显摆,他邀请小绵羊一起玩个游戏。游戏一开始,Lostmonkey在地上沿着一条直线摆上n个装置,每个装置设定初始弹力系数ki,当绵羊达到第i个装置时,它会往后弹ki步,达到第i+ki个装置,若不存在第i+ki个装置,则绵羊被弹飞。绵羊想知道当它从第i个装置起步时,被弹几次后会被弹飞。为了使得游戏更有趣,Lostmonkey可以修改某个弹力装置的弹力系数,任何时候弹力系数均为正整数。

Input

第一行包含一个整数n,表示地上有n个装置,装置的编号从0到n-1,接下来一行有n个正整数,依次为那n个装置的初始弹力系数。第三行有一个正整数m,接下来m行每行至少有两个数i、j,若i=1,你要输出从j出发被弹几次后被弹飞,若i=2则还会再输入一个正整数k,表示第j个弹力装置的系数被修改成k。

Output

对于每个i=1的情况,你都要输出一个需要的步数,占一行。

Sample Input

4
1 2 1 1
3
1 1
2 1 1
1 1

Sample Output

2
3

HINT

对于20%的数据n,m<=10000,
对于100%的数据n<=200000,m<=100000




这道题本是一道LCT的题,但用分块就可以过了。。。

分块大法好!

我们对于每一个位置都维护两个信息:
1、跳出其所在块需要的步数(step)
2、跳出其所在块后跳到的位置(where)
询问的话就不断地跳并累加答案。
修改的话就暴力重构当先点到其所在块的左端点的信息。
这两个操作都可以在根号时间做完。
上标:

#include<cstdio>
#include<cmath>
#define N 200010
using namespace std;
struct node{int step,where;}b[N];
int n,m,a[N],bl[N],l[N],r[N],st;

inline int read()
{
	int x=0; char c=getchar();
	while (c<'0' || c>'9') c=getchar();
	while (c>='0' && c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
	return x;
}

void init()
{
	for (int i=1;i<=bl[n];i++)
		for (int j=r[i];j>=l[i];j--)
			if (j+a[j]>r[i]) b[j]=(node){1,j+a[j]};
			else b[j]=(node){b[j+a[j]].step+1,b[j+a[j]].where};
}

void query(int x)
{
	int s=0;
	for (int i=x;i<=n;i=b[i].where) s+=b[i].step;
	printf("%d
",s);
}

void change(int x)
{
	for (int i=x;i>=l[bl[x]];i--)
		if (i+a[i]>r[bl[x]]) b[i]=(node){1,i+a[i]};
		else b[i]=(node){b[i+a[i]].step+1,b[i+a[i]].where};
}

int main()
{
	freopen("2002.in","r",stdin);
	freopen("2002.out","w",stdout);
	n=read(),st=sqrt(n);
	for (int i=1;i<=n;i++)
	{
		a[i]=read(),bl[i]=(i-1)/st+1;
		if (!l[bl[i]]) l[bl[i]]=i;
		r[bl[i]]=i;
	}
	init();
	m=read();
	for (int i=1,tt,x,y;i<=m;i++)
	{
		tt=read();
		if (tt==1) x=read()+1,query(x);
		else x=read()+1,a[x]=read(),change(x);
	}
	return 0;
}
转载需注明出处。
原文地址:https://www.cnblogs.com/jz929/p/11817567.html