题意
n(n≤105)个洞排成一条直线,第ii个洞有力量值ai,当一个球掉进洞ii时就会被立刻弹到i+ai,直到超出n。进行m(m≤105)次操作:
·修改第i个洞的力量值ai。
·在洞xx上放一个球,问该球几次后被哪个洞弹飞出界。
思路
分块暴力,每个块内维护两个信息(块内DP可以求出):
①从当前位置跳出块内需要跳几次num[i]
②从当前块内跳出的下一个位置jump[i]
修改:
只需要将修改元素所在块内信息更新一次即可,单次时间复杂O(√n)
查询:
由起点i不停往下一个块跳jump[i],并且累计次数num[i],在最后一个块内暴力便利一遍就行了
#include<iostream> #include<algorithm> #include<cmath> using namespace std; const int maxn=1e5+10; int n,m,bl,bel[maxn],x,y; int a[maxn],num[maxn],jump[maxn]; void update(int x) { int fr=x*bl,to=fr+bl-1;if(to>n) to=n; for(int i=to;i>=fr;i--){ if(a[i]+i>to){ num[i]=1; jump[i]=a[i]+i; } else{ num[i]=num[i+a[i]]+1; jump[i]=jump[i+a[i]]; } } } void query(int x) { int ans=0,i,pre; for(i=x;i<=n;i=jump[i]) ans+=num[i],pre=i; while(pre+a[pre]<=n) pre+=a[pre]; printf("%d %d ",pre,ans); } int main() { scanf("%d%d",&n,&m); int op; for(int i=1;i<=n;i++){ scanf("%d",&a[i]); num[i]=0; jump[i]=i; } bl=sqrt(n); for(int i=0;i<=n/bl;i++) update(i); while(m--){ scanf("%d",&op); if(op==0){ scanf("%d%d",&x,&y); a[x]=y; update(x/bl); } else{ scanf("%d",&x); query(x); } } return 0; }