lct做法请点这里
题目大意:
Lostmonkey在地上沿着一条直线摆上n个装置,每个装置设定初始弹力系数ki,当绵羊达到第i个装置时,它会往后弹ki步,达到第i+ki个装置,若不存在第i+ki个装置,则绵羊被弹飞。绵羊想知道当它从第i个装置起步时,被弹几次后会被弹飞。为了使得游戏更有趣,Lostmonkey可以修改某个弹力装置的弹力系数,任何时候弹力系数均为正整数。
bzoj传送门
思路:
分块,每一个块里面维护两个东西,第一个是块内的每一个节点能够在快内跳过的次数,第二是块内的每一个节点跳出块了之后跳到的下一个块的位置,每次查询的时候只需要遍历 n−−√ n 个块,每次修改的时候只需要 O(n−−√) O ( n ) 的吧暴力修改每一个块内的两个数组的情况就可以了。所以总共的时间复杂度是 O(m∗n−−√) O ( m ∗ n ) 。想法是非常非常地简单好理解,但是实现起来还是有几个小的细节需要注意。
/*==========================
* Author : ylsoi
* Problem : [hnoi2010]Bounce
* File : [hnoi2010]Bounce.cpp
* Algorithm : Block
* Time : 2018.4.5
* ========================*/
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<ctime>
using namespace std;
void File(){
#ifndef ONLINE_JUDGE
freopen("[hnoi2010]Bounce.in","r",stdin);
freopen("[hnoi2010]Bounce.out","w",stdout);
#endif
}
template<typename T>void chckmax(T &_,T __){_=_>__ ? _ : __;}
template<typename T>void chckmin(T &_,T __){_=_>__ ? _ : __;}
#define REP(i,a,b) for(register int i=a;i<=b;++i)
#define DREP(i,a,b) for(register int i=a;i>=b;--i)
#define MREP(i,x) for(register int i=beg[x];i;i=E[i].last)
#define mem(a) memset(a,0,sizeof(a))
#define ll long long
#define inf (0x3f3f3f3f)
const int maxn=2e5+10;
const int maxb=450+10;
int n,m,be[maxn],at[maxn],block,ans;
struct Block{
int cnt[maxb],a[maxb],dis[maxb],len;
inline void update(){
DREP(i,len,1){
if(i+a[i]>len){
dis[i]=a[i];
cnt[i]=1;
}
else{
dis[i]=dis[i+a[i]]+a[i];
cnt[i]=cnt[i+a[i]]+1;
}
}
}
inline int ask(int x){
ans+=cnt[x];
return dis[x];
}
}b[maxb];
int main(){
File();
scanf("%d",&n);
block=pow(n,1.0/2);
REP(i,1,n){
be[i]=ceil(i*1.0/block);
at[i]=(i-1)%block+1;
}
REP(i,1,n){
scanf("%d",&b[be[i]].a[at[i]]);
++b[be[i]].len;
}
for(int i=1;;++i){
if(!b[i].len)break;
b[i].update();
}
scanf("%d",&m);
int key,u,v;
REP(i,1,m){
scanf("%d",&key);
if(key==1){
ans=0;
scanf("%d",&u);++u;
while(u<=n)u+=b[be[u]].ask(at[u]);
printf("%d\n",ans);
}
else{
scanf("%d%d",&u,&v);++u;
b[be[u]].a[at[u]]=v;
b[be[u]].update();
}
}
return 0;
}