Description
\(n(n\leq10^5)\)个洞排成一条直线,第\(i\)个洞有力量值\(a_i\),当一个球掉进洞\(i\)时就会被立刻弹到\(i+a_i\),直到超出\(n\)。进行\(m(m\leq10^5)\)次操作:
- 修改第\(i\)个洞的力量值\(a_i\)。
- 在洞\(x\)上放一个球,问该球几次后被哪个洞弹飞出界。
Solution
将\(n\)个洞分成大小为\(\sqrt n\)的\(\sqrt n\)个块。
\(c[i]\)记录\(i\)要跳出所在的块需要多少次,\(nxt[i]\)记录跳出到哪个点。
修改时,从后到前重构该块内所有点的\(c[i]\)和\(nxt[i]\),其他块不受影响。
查询时,由\(i\)跳到\(nxt[i]\)并累加\(c[i]\),在即将出界\((nxt[i]>n)\)前一步一步跳来得知是哪个洞将它弹出界的。
时间复杂度\(O(m\sqrt n)\)。
Code
//Holes
#include <cstdio>
#include <cmath>
inline char gc()
{
static char now[1<<16],*S,*T;
if(S==T) {T=(S=now)+fread(now,1,1<<16,stdin); if(S==T) return EOF;}
return *S++;
}
inline int read()
{
int x=0,f=1; char ch=gc();
while(ch<'0'||'9'<ch) {if(ch=='-') f=-1; ch=gc();}
while('0'<=ch&&ch<='9') x=x*10+ch-'0',ch=gc();
return x*f;
}
int const N=1e5+10;
int n,m,n0;
int p[N],nxt[N],c[N];
void update(int t)
{
int fr=t*n0,to=fr+n0-1; if(to>n) to=n;
for(int i=to;i>=fr;i--)
if(i+p[i]>to) nxt[i]=i+p[i],c[i]=1;
else nxt[i]=nxt[i+p[i]],c[i]=1+c[i+p[i]];
}
int main()
{
n=read(),m=read(); n0=sqrt(n);
for(int i=1;i<=n;i++) p[i]=read(),nxt[i]=i,c[i]=0;
for(int t=0;t<=n/n0;t++) update(t);
for(int i=1;i<=m;i++)
{
int opt=read();
if(opt==0)
{
int x=read(),y=read();
p[x]=y; update(x/n0);
}
else
{
int res=0,pre;
for(int x=read();x<=n;x=nxt[x]) pre=x,res+=c[x];
while(pre+p[pre]<=n) pre+=p[pre];
printf("%d %d\n",pre,res);
}
}
return 0;
}