题目描述
某天,Lostmonkey发明了一种超级弹力装置,为了在他的绵羊朋友面前显摆,他邀请小绵羊一起玩个游戏。游戏一开始,Lostmonkey在地上沿着一条直线摆上n个装置,每个装置设定初始弹力系数ki,当绵羊达到第i个装置时,它会往后弹ki步,达到第i+ki个装置,若不存在第i+ki个装置,则绵羊被弹飞。绵羊想知道当它从第i个装置起步时,被弹几次后会被弹飞。为了使得游戏更有趣,Lostmonkey可以修改某个弹力装置的弹力系数,任何时候弹力系数均为正整数。
输入格式
第一行包含一个整数n,表示地上有n个装置,装置的编号从0到n-1。
接下来一行有n个正整数,依次为那n个装置的初始弹力系数。
第三行有一个正整数m,
接下来m行每行至少有两个数i、j,若i=1,你要输出从j出发被弹几次后被弹飞,若i=2则还会再输入一个正整数k,表示第j个弹力装置的系数被修改成k。
输出格式
对于每个i=1的情况,你都要输出一个需要的步数,占一行。
输入输出样例
输入 #1 复制
4
1 2 1 1
3
1 1
2 1 1
1 1
输出 #1 复制
2
3
说明/提示
对于20%的数据n,m<=10000,对于100%的数据n<=200000,m<=100000
题解:
首先,测试时想到的是修改优化,查询O(1),建虚拟节点,删边,连边,却不知怎么实现,直到
@abcde 讲是LCT裸版题,才。。以后有空再补吧,
言归正传,区间询问,修改应第一反应分块||莫队,且这道题维护的不是树,而是一维区间,且是跳到尾端的次数,很适合分块。
于是 nxt[i] 保存i跳到下一块的某位,cnt[i] 保存i跳出当前块所需的步数
查询时logn跳,修改时暴力修改区间logn
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<cstdio>
#define N 200005
#define log_N 505
using namespace std;
int bl[log_N],br[log_N],b_num,nxt[N],cnt[N],n,m,a[N],belong[N];
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
int leng=(int)sqrt(n*1.0);
for(int i=1;i<=n;){
if(i+leng-1>n)break;
bl[++b_num]=i;br[b_num]=i+leng-1;
for(int j=bl[b_num];j<=br[b_num];j++){
belong[j]=b_num;
}
i=i+leng;
}
//cout<<leng<<endl;
if(br[b_num]<n){
bl[b_num+1]=br[b_num]+1;
br[++b_num]=n;
for(int i=bl[b_num];i<=br[b_num];i++){
belong[i]=b_num;
}
}
for(int i=n;i;--i){
if(i+a[i]>br[belong[i]]){
cnt[i]=1;nxt[i]=i+a[i];
}else{
cnt[i]=cnt[i+a[i]]+1;
nxt[i]=nxt[i+a[i]];
}
}
// cout<<nxt[1]<<endl;
// for(int i=1;i<=n;i++)cout<<nxt[i]<<" ";
scanf("%d",&m);
// cout<<m<<endl;
for(int j=1;j<=m;++j){
int op,x,val;
scanf("%d",&op);
if(op==1){
scanf("%d",&x);
++x;
int ans=0;
while(x<=n){
ans+=cnt[x];
//if(nxt[x]<=x)break;
x=nxt[x];
}//cout<<x<<endl;
printf("%d\n",ans);
}else{
scanf("%d%d",&x,&val);
++x;
a[x]=val;
//cout<<br[belong[x]]<<" "<<bl[belong[x]]<<endl;
for(int i=br[belong[x]];i>=bl[belong[x]];--i){
if(i+a[i]>br[belong[i]]){
cnt[i]=1;nxt[i]=i+a[i];
}else{
cnt[i]=cnt[i+a[i]]+1;
nxt[i]=nxt[i+a[i]];
}
}
}
//cout<<op<<endl;
}
return 0;
}