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\n",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;
}
转载需注明出处。