题意:
某天,Lostmonkey发明了一种超级弹力装置,为了在他的绵羊朋友面前显摆,他邀请小绵羊一起玩个游戏。游戏一开始,Lostmonkey在地上沿着一条直线摆上n个装置,每个装置设定初始弹力系数ki,当绵羊达到第i个装置时,它会往后弹ki步,达到第i+ki个装置,若不存在第i+ki个装置,则绵羊被弹飞。绵羊想知道当它从第i个装置起步时,被弹几次后会被弹飞。为了使得游戏更有趣,Lostmonkey可以修改某个弹力装置的弹力系数,任何时候弹力系数均为正整数。
分析:
将序列分块,每块sqrt(n)个
在每个块中维护f[i],to[i]
f[i] 表示跳几次可以跳出所在块
to[i] 表示跳出所在块后到达的位置
在查询时,我们O(sqrt(n))的时间进行“整块”的模拟,可以得到结果
在修改i时,我们只需维护一下(l[belong[i]]--i)的序列就可以了
总复杂度:O(n*sqrt(n))
理解起来很容易,代码也很简单,分块大法好啊。
LCT也可以做,下次补上。
参考:https://blog.csdn.net/kaisa158/article/details/47264093。
代码:
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6+5;
int f[N],to[N],k[N],bl[N];
int n,m,tot,l[N];
//将序列分块,每块sqrt(n)个。
//在每个块中维护f[i],to[i]
//f[i] 表示跳几次可以跳出所在块
//to[i] 表示跳出所在块后到达的位置
//bl[i] 表示第i个数所属块
//l[i] 表示第i个块的左端点
///预处理
void Init(){
int i,j;
scanf("%d",&n);
m=int(sqrt(n)); j=m;
for (i=0;i<n;i++){
if (j==m)
j=1,tot++,l[tot]=i;
else j++;
scanf("%d",k+i);
bl[i]=tot;
}
l[tot+1]=n;
for (i=n-1;i>=0;i--)
if (i+k[i]>=l[bl[i]+1]) f[i]=1,to[i]=i+k[i];
else f[i]=f[i+k[i]]+1,to[i]=to[i+k[i]];
}
int qu(int p){
int s=0;
while (p<n) {s+=f[p];p=to[p];}
return s;
}
void up(int p,int k1){
k[p]=k1; int i;
for (i=p;i>=l[bl[p]];i--)
if (i+k[i]>=l[bl[i]+1]) f[i]=1,to[i]=i+k[i];
else f[i]=f[i+k[i]]+1,to[i]=to[i+k[i]];
}
void sv(){
int x,y,z;
scanf("%d",&m);
for(int i=0;i<m;i++){
scanf("%d",&x);
if (x==1){
scanf("%d",&y);
printf("%d\n",qu(y));
}
else{
scanf("%d%d",&y,&z);
up(y,z);
}
}
}
int main(){
Init();
sv();
return 0;
}