BZOJ 2002 [Hnoi2010]Bounce 弹飞绵羊 (分块)
题意
给你一组正整数,从i位置可以走到i+a[i],两种操作:
1.从x走几步能走出n
2.将某个a[x]改为y
思路
考虑分块,分块大小为根号n,那么每个点需要处理:出本块需要的步数、出本块到达的地方
对于每一块内的元素,维护的这两个信息都是独立的
那么对于操作1:模拟一遍块间移动即可,复杂度\(O(\sqrt{n})\)
操作2:将x所在的块内所有元素的信息更新,复杂度\(O(\sqrt{n})\)
代码
int n,t;
int a[maxn];
int to[maxn],cnt[maxn];
int go(int x){
if(x>n)return -1;
return (x+t-1)/t;
}
int main() {
scanf("%d", &n);
t = sqrt(n);
for(int i = 1; i <= n; i++){
scanf("%d", &a[i]);
}
int tot = (n+t-1)/t;
for(int i = 1; i <= tot; i++){
int l = (i-1)*t+1, r = min(n,i*t);
for(int j = r; j >= l; j--){
int ot = j+a[j];
if(ot>r){
cnt[j]=1;to[j]=ot;
}
else{
cnt[j]=cnt[ot]+1;to[j]=to[ot];
}
}
}
int q;
scanf("%d", &q);
while(q--){
int op,x,y;
scanf("%d %d", &op, &x);
x++;
if(op==1){
int ans = 0;
while(x<=n){
ans+=cnt[x];
x=to[x];
}
printf("%d\n",ans);
}
else {
scanf("%d", &y);
a[x]=y;
int l = (go(x)-1)*t+1,r=min(n,go(x)*t);
for(int j = r; j >= l; j--){
int ot = j+a[j];
if(ot>r){
cnt[j]=1;to[j]=ot;
}
else{
cnt[j]=cnt[ot]+1;to[j]=to[ot];
}
}
}
}
return 0;
}