分块好题
我们将序列划分成sqtr(n)个小块,维护每个块内的东西
我们维护两个值to[i]和f[i],to[i]表示当前点能到达的第一个块外的点,f[i]表示需要多少次能跳出去,难么显然,我们查询一个点的时候,只需要通过to[i]最多转移sqrt(n)次就能跳出去,修改的话,我们不难发现,一个点,只有一个指向关系和一个被指向关系,暴力修改就好了
代码
//By AcerMo
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int M=3e5+10;
inline int read()
{
int x=0;char ch=getchar();
while(ch>'9'||ch<'0')ch=getchar();
while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
return x;
}
int n,m,block,cnt;
int l[1005],r[1005];
int to[M],f[M],a[M],bloc[M];
inline int cal(int x)
{
int tmp=0;
while(x) tmp+=f[x],x=to[x];
return tmp;
}
inline void change(int x,int y)
{
a[x]=y;
for(int i=x;i>=l[bloc[x]];i--)
if(bloc[i]==bloc[i+a[i]])
f[i]=f[i+a[i]]+1,to[i]=to[i+a[i]];
else f[i]=1,to[i]=i+a[i];
return ;
}
int main()
{
n=read();block=sqrt(n);
for(int i=1;i<=n;i++) a[i]=read();
if(n%block) cnt=n/block+1;
else cnt=n/block;
for(int i=1;i<=cnt;i++)
l[i]=(i-1)*block+1,r[i]=i*block;
r[cnt]=n;
for(int i=1;i<=n;i++)
bloc[i]=(i-1)/block+1;
for(int i=n;i>0;i--)
{
if(i+a[i]>n)f[i]=1;
else if(bloc[i]==bloc[i+a[i]])
f[i]=f[i+a[i]]+1,to[i]=to[i+a[i]];
else f[i]=1,to[i]=i+a[i];
}
m=read();
for(int i=1;i<=m;i++)
{
int flag=read(),x=read(),y;x++;
if(flag==1)printf("%d\n",cal(x));
else y=read(),change(x,y);
}
return 0;
}