题意:
给定长度为n的序列a,a(i)表示将石子丢到i,会从i位置会跳到i+a(i),一直重复直到跳出n
m次操作,操作有两种:
1.将a[x]修改为y
2.询问将式子丢到x,最后一步从哪个位置跳出n,以及总共跳了多少步
数据范围:n,m<=1e5
解法:
分块
对于每个块,预处理:
cnt[i]表示从i跳出当前块需要的次数
nt[i]表示从i跳出当前块之后到达的位置
last[i]变送从i跳出当前块最后一步的位置
对于修改操作,重新处理a[x]所在块,复杂度O(sq)
对于查询操作,是按块跳的,最多跳sq次,复杂度O(sq)
总复杂度O(m*sq)
总结:
当块与块之间相互不影响时,就可以用分块做
code:
#include<bits/stdc++.h>
using namespace std;
const int maxm=1e5+5;
int cnt[maxm];//cnt[i]表示从i跳出当前块需要的次数
int nt[maxm];//nt[i]表示从i跳出当前块之后到达的位置
int last[maxm];//last[i]变送从i跳出当前块最后一步的位置
//
int belong[maxm];
int l[maxm],r[maxm];
int block;
int num;
//
int a[maxm];
int n,m;
//
void build(){
block=sqrt(n);
num=n/block+(n%block!=0);
for(int i=1;i<=num;i++){
l[i]=(i-1)*block+1;
r[i]=i*block;
}
r[num]=n;
for(int i=1;i<=n;i++){
belong[i]=(i-1)/block+1;
}
//
for(int i=n;i>=1;i--){
if(i+a[i]>r[belong[i]]){
cnt[i]=1;
nt[i]=min(i+a[i],n+1);
last[i]=i;
}else{
cnt[i]=cnt[i+a[i]]+1;
nt[i]=nt[i+a[i]];
last[i]=last[i+a[i]];
}
}
}
void update(int x,int y){//重新构造这个块
a[x]=y;
for(int i=r[belong[x]];i>=l[belong[x]];i--){
if(i+a[i]>r[belong[i]]){
cnt[i]=1;
nt[i]=min(i+a[i],n+1);
last[i]=i;
}else{
cnt[i]=cnt[i+a[i]]+1;
nt[i]=nt[i+a[i]];
last[i]=last[i+a[i]];
}
}
}
void ask(int x){
int out=x,num=0;
int now=x;
while(now!=n+1){
out=last[now];
num+=cnt[now];
now=nt[now];
}
printf("%d %d\n",out,num);
}
signed main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
build();
while(m--){
int op;scanf("%d",&op);
if(op==0){//a[x]=y
int x,y;scanf("%d%d",&x,&y);
update(x,y);
}else{
int x;scanf("%d",&x);
ask(x);
}
}
return 0;
}