您的位置:首页 > 其它

Bzoj 2002: [Hnoi2010]Bounce 弹飞绵羊(分块)

2017-02-27 10:58 393 查看
2002: [Hnoi2010]Bounce 弹飞绵羊

Time Limit: 10 Sec Memory Limit: 259 MB

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。对于20%的数据n,m<=10000,对于100%的数据n<=200000,m<=100000

Output

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

Sample Input

4

1 2 1 1

3

1 1

2 1 1

1 1

Sample Output

2

3

/*
分块.
没有修改的话我们可以O(n)暴力递推出每个块的贡献.
然后O(1)查询.
这个题有修改操作如果那样做的话复杂度可能会到O(n).
然后我们可以分块O(√n)的时间内完成这两个操作.
对于每个点维护一个跳出这个块的第一个位置和在这个中的移动次数.
修改操作的时候要倒序修改
这样倒序递推比较方便....
splay启发式合并什么的以后再写.
*/
#include<iostream>
#include<cstdio>
#include<cmath>
#define MAXN 200001
using namespace std;
int next[MAXN],belong[MAXN],n,m,ans,k[MAXN],len,s[MAXN];
int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar();
return x*f;
}
void slove(int x)
{
int tot,pos;
for(int i=(x-1)*len+1;i<=min(n,x*len);i++)
{
tot=0,pos=i;//位置
while(pos<=n&&belong[pos]==x) tot++,pos+=k[pos];
s[i]=tot,next[i]=pos;
}
return ;
}
int query(int x)
{
ans=0;
for(int i=x;i<=n;i=next[i]) ans+=s[i];
return ans;
}
void _union(int x,int y)
{
k[x]=y;
for(int i=x;i>=(belong[x]-1)*len+1;i--)//倒序递推
{
if(i+k[i]>belong[x]*len) s[i]=1,next[i]=i+k[i];
else s[i]=s[i+k[i]]+1,next[i]=next[i+k[i]];
}
return ;
}
int main()
{
int x,y,z;
n=read();len=sqrt(n);
for(int i=1;i<=n;i++)
{
k[i]=read();
belong[i]=(i-1)/len+1;//每块的
}
for(int i=1;i<=belong
;i++) slove(i);
m=read();
for(int i=1;i<=m;i++)
{
z=read(),x=read();x++;
if(z&1) printf("%d\n",query(x));
else y=read(),_union(x,y);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: