题面
某天,Lostmonkey发明了一种超级弹力装置,为了在他的绵羊朋友面前显摆,他邀请小绵羊一起玩个游戏。游戏一开始,Lostmonkey在地上沿着一条直线摆上n个装置,每个装置设定初始弹力系数ki,当绵羊达到第i个装置时,它会往后弹ki步,达到第i+ki个装置,若不存在第i+ki个装置,则绵羊被弹飞。绵羊想知道当它从第i个装置起步时,被弹几次后会被弹飞。为了使得游戏更有趣,Lostmonkey可以修改某个弹力装置的弹力系数,任何时候弹力系数均为正整数。
对于20%的数据n,m<=10000,对于100%的数据n<=200000,m<=100000
分析
经典分块。本来是有两种暴力求解的方法,
- 修改时O(N)求出每个点弹出去的次数,可以O(1)查询。
- 查询时O(N)模拟,计算弹出去的次数,可以O(1)修改。
于是我们需要把复杂度均摊一下,就用到了分块。在每一个块内维护弹出这个块需要的次数,以及掉到哪里。
修改的时候也只需要修改这一个块里的点弹出去的次数和弹到的位置。最后的时间复杂度就(N√N)
代码
- #include<bits/stdc++.h>
- using namespace std;
- #define N 200020
- int n,m,t,op,x,y;
- int k[N],pos[N],L[N],R[N],step[N],nxt[N];
- template<class T>
- inline void read(T &x)
- {
- x=0;static char c=getchar();
- while(c<'0'||c>'9') c=getchar();
- while(c>='0'&&c<='9')x=x*10+c-'0',c=getchar();
- }
- inline int cal(int x)
- {
- int tmp=0;
- while(1)
- {
- tmp+=step[x];
- if(nxt[x]==0)break;
- x=nxt[x];
- }
- return tmp;
- }
- int main()
- {
- read(n);t=sqrt(n);
- for(int i=1;i<=n;i++)read(k[i]);
- for(int i=1;i<=t;i++)L[i]=(i-1)*t+1,R[i]=i*t;
- if(R[t]<n)t++,L[t]=R[t-1]+1,R[t]=n;
- for(int i=1;i<=t;i++)
- for(int j=L[i];j<=R[i];j++)
- pos[j]=i;
- for(int i=n;i>=1;i--)
- {
- if(i+k[i]>n)step[i]=1;
- else if(pos[i]==pos[i+k[i]])step[i]=step[i+k[i]]+1,nxt[i]=nxt[i+k[i]];
- else step[i]=1,nxt[i]=i+k[i];
- }
- read(m);
- while(m--)
- {
- read(op);read(x);x++;
- if(op==1)printf("%d ",cal(x));
- else
- {
- read(y);k[x]=y;
- for(int i=x;i>=L[pos[x]];i--)
- if(pos[i]==pos[i+k[i]])step[i]=step[i+k[i]]+1,nxt[i]=nxt[i+k[i]];
- else step[i]=1,nxt[i]=i+k[i];
- }
- }
- return 0;
- }