题意
某天,Lostmonkey发明了一种超级弹力装置,为了在他的绵羊朋友面前显摆,他邀请小绵羊一起玩个游戏。游戏一开始,Lostmonkey在地上沿着一条直线摆上n个装置,每个装置设定初始弹力系数ki,当绵羊达到第i个装置时,它会往后弹ki步,达到第i+ki个装置,若不存在第i+ki个装置,则绵羊被弹飞。绵羊想知道当它从第i个装置起步时,被弹几次后会被弹飞。为了使得游戏更有趣,Lostmonkey可以修改某个弹力装置的弹力系数,任何时候弹力系数均为正整数。
题解
考虑分块维护区间信息,单次询问和修改可以做到
n−−√
n
的复杂度。
对于块内任意一个位置,维护两个两个信息,一个是经过多少次弹出本块,另一个是弹出本块外的落点。
对于询问操作,只需要遍历每块中的落点,直至弹出序列,单次询问需要
n−−√
n
次转移。
对于修改操作,除了修改对应位置的弹力系数,还需要修改块内的信息,因为对应位置的弹力系数变了,块内其他位置落到本位置的信息也变了(如经过多少次弹出本块),注意到块外的信息没有变,所以只需要
n−−√
n
次修改本块内的信息。
代码
#include<bits/stdc++.h>
using namespace std;
const int nmax = 2e5+10;
const int INF = 0x3f3f3f3f;
const int mm = 1000;
int n,m;
int num,block,L[mm],R[mm],belong[nmax],rx[nmax],step[nmax],nxtpos[nmax];
void build(){
block = 3*sqrt(n);
num = n / block; if(n%block) num++;
for(int i = 1;i<=num;++i){
L[i] = (i-1) * block + 1;
R[i] = i*block;
}
R[num] = n;
}
void init(){
for(int i = num;i>=1;--i){
for(int j = R[i];j>=L[i];--j){
if(j + rx[j] > n) nxtpos[j] = n+1, step[j] = 1;
else{
int pos = j + rx[j];
if(belong[pos] == belong[j]) nxtpos[j] = nxtpos[pos],step[j] = step[pos] + 1;
else nxtpos[j] = pos, step[j] = 1;
}
}
}
}
int main() {
scanf("%d",&n);
build();
for(int i = 1;i<=n;++i){
scanf("%d",&rx[i]);
belong[i] = (i-1) / block + 1;
}
init();
scanf("%d",&m);
int op,a,b;
for(int i = 1;i<=m;++i){
scanf("%d %d",&op,&a); a++;
if(op==1){
int pos = a,cnt = 0;
for(int j = a;j<=n;j=nxtpos[j]) cnt += step[j];
printf("%d\n",cnt);
}else{
scanf("%d",&b);
rx[a] = b;
for(int j = a;j>=1;--j){
if(belong[j] != belong[a]) break;
else{
int pos = j+rx[j];
if(belong[pos] != belong[j]) step[j] = 1, nxtpos[j] = pos;
else step[j] = step[pos] + 1, nxtpos[j] = nxtpos[pos];
}
}
}
}
return 0;
}