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;
}
posted @ 2020-05-27 22:36  wrjlinkkkkkk  阅读(148)  评论(0编辑  收藏  举报