- 题意(原题):
- 一只绵羊,在i会被D飞到i+a[i]。操作有询问从i出发被D多少次以后出了数列,或者改a[i]。n<=200000,m<=100000。
- 思路:
- 分块,每个块大小sqrt(n)+1。jump[i]表示从i开始跳,直到跳出i所在的块后的位置。jumpNum[i]表示跳出i所在的块要跳多少次。对于询问就每次加上jumpNum[i]然后跳到jump[i],直到跳出去;对于修改,从i依次更新到i所在的块的块头的jump和jumpNum。
- 复杂度分析:
- 初始化O(n),对于询问至多跳sqrt(n)(差不多就是n/size)个块,对于更改只会影响一个块内至多sqrt(n)+1的元素。所以复杂度为O(m*sqrt(n)+n)。大概四千四百万的样子。
- 代码:
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
using namespace std;
int a[210000],bl[210000];
int jump[210000],jumpNum[210000];
int main()
{
memset(jumpNum,0,sizeof(jumpNum));
int n,m,size;
scanf("%d",&n);
size=sqrt(n)+1;
for(int i=0;i<n;i++)
scanf("%d",&a[i]);
for(int i=n-1;i>=0;i--)
{
if(i+a[i]>n-1)
{
jump[i]=-1;
jumpNum[i]=1;
continue;
}
if(i+a[i]>=(i/size+1)*size)
{
jump[i]=i+a[i];
jumpNum[i]=1;
}
else
{
jump[i]=jump[i+a[i]];
jumpNum[i]=jumpNum[i+a[i]]+1;
}
}
scanf("%d",&m);
while(m--)
{
int op,x,y;
scanf("%d%d",&op,&x);
if(op==2)
scanf("%d",&y);
if(op==1)
{
int now=x,ans=0;
for(;;)
{
ans+=jumpNum[now];
if(jump[now]==-1)
break;
else
now=jump[now];
}
printf("%d\n",ans);
}
else
{
a[x]=y;
for(int i=x;i>=(x/size*size);i--)
{
if(i+a[i]>n-1)
{
jump[i]=-1;
jumpNum[i]=1;
continue;
}
if(i+a[i]>=(i/size+1)*size)
{
jump[i]=i+a[i];
jumpNum[i]=1;
}
else
{
jump[i]=jump[i+a[i]];
jumpNum[i]=jumpNum[i+a[i]]+1;
}
}
}
}
return 0;
}