【bzoj2002】弹飞绵羊(分块)

题目分析

  题意:每个点都有一个值$v_i$,从一个点出发,每走到一个点,会跳到i+vi的位置,问需要跳多少次能跳出n?带修改。

  此题可以用lct做,此处使用了分块:将序列分块后,每个点记录从此点最少跳几次能跳出当前块,和跳出后到达的位置,倒叙可以优化。

  这样询问时只要一直跳至多$\sqrt{n}$个块就能知道答案。

  对于修改操作,只需将修改点和修改点之前的当前块元素重新使用上述处理,同样倒叙可以优化。

code

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
#include<vector>
using namespace std;

const int N = 2e5 + 5;
int n, m, S;
int blkCnt, blk[N], bl[500], br[500];
long long val[N], step[N], des[N];

inline int read(){
    int i = 0, f = 1; char ch = getchar();
    for(; (ch < '0' || ch > '9') && ch != '-'; ch = getchar());
    if(ch == '-') f = -1, ch = getchar();
    for(; ch >= '0' && ch <= '9'; ch = getchar())
        i = (i << 3) + (i << 1) + (ch - '0');
    return i * f;
}

inline long long readL(){
    long long i = 0, f = 1; char ch = getchar();
    for(; (ch < '0' || ch > '9') && ch != '-'; ch = getchar());
    if(ch == '-') f = -1, ch = getchar();
    for(; ch >= '0' && ch <= '9'; ch = getchar())
        i = (i << 3) + (i << 1) + (ch - '0');
    return i * f;
}

inline void wr(long long x){
    if(x < 0) putchar('-'), x = -x;
    if(x > 9) wr(x / 10);
    putchar(x % 10 + '0');
}

inline void init(){
    bl[blkCnt = 1] = 1;
    for(int i = 1; i <= n; i++){
        if(i % S == 0){
            br[blkCnt] = i;
            blk[i] = blkCnt;
            if(i + 1 <= n){
                bl[++blkCnt] = i + 1;
                continue;
            }
        }
        blk[i] = blkCnt;
    }
    br[blkCnt] = n;
}

int main(){
    n = read(), S = sqrt(n);
    for(int i = 1; i <= n; i++) val[i] = readL();
    init();
    for(int i = n; i >= 1; i--){
        int tmp = i;
        bool flag = false;
        step[i] = 0;
        while(tmp + val[tmp] <= br[blk[i]]){
            tmp += val[tmp], step[i]++;
            if(step[tmp]){
                step[i] += step[tmp], des[i] = des[tmp], flag = true;
                break;
            }
        }
        if(!flag) des[i] = tmp + val[tmp], step[i]++;
    }
    m = read();
    for(int t = 1; t <= m; t++){
        int opt = read();
        if(opt == 1){
            int src = read() + 1, ans = 0, tmp;
            tmp = src;
            while(des[tmp] <= n) ans += step[tmp], tmp = des[tmp];
            ans += step[tmp];
            wr(ans), putchar('\n');
        }
        else{
            int pos = read() + 1, value = readL();
            if(val[pos] == value) continue;
            val[pos] = value;
            for(int i = bl[blk[pos]]; i <= pos; i++) step[i] = 0;
            
            for(int i = pos; i >= bl[blk[pos]]; i--){
                int tmp = i; bool flag = false;
                while(tmp + val[tmp] <= br[blk[pos]]){
                    tmp += val[tmp], step[i]++;
                    if(step[tmp]){
                        step[i] += step[tmp], des[i] = des[tmp], flag = true;
                        break;
                    }
                }
                if(!flag) des[i] = tmp + val[tmp], step[i]++;
            }
        }
    }
    return 0;
}
posted @ 2017-07-29 23:06  CzYoL  阅读(113)  评论(0编辑  收藏