「Bzoj 2002」「HNOI2010」弹飞绵羊 (LCT / 分块)

BZOJ 2002
题意:见上。

如果没有修改就是个递推……
有修改的话我们将这个递推状态抽象成图论的点,然后发现这是一棵树
然后修改相当于断边连边,那么就是 LCT 裸题了。

这题也可以分块,每个块维护

  • $f[i]$表示从$i$开始,跳出所在块的步数
  • $to[i]$表示跳出所在块后到了哪里

知识点
1、递推/DP等状态抽象成图论的点,树、DAG图等

#include<cstdio> 
#include<cstring>
#include<algorithm>
#include<iostream>
#include<vector>
#include<map>
#include<stack>
#define ms(i, j) memset(i, j, sizeof i)
#define LL long long
#define db long double
#define fir first
#define sec second
#define mp make_pair
using namespace std;

namespace flyinthesky {

    #define lc ch[x][0]
    #define rc ch[x][1]

    const int MAXN = 200000 + 5;

    int ch[MAXN][2], fa[MAXN], sumv[MAXN], rev[MAXN];
    // ch: ??????,fa: ????,val: ???,xorv: ?????, rev: ???? 
    int n, q, top, stk[MAXN];
    int ki[MAXN];

    int isRt(int x) {return ch[fa[x]][0] != x && ch[fa[x]][1] != x;} //???Splay??? 
    int rel(int x) {return ch[fa[x]][1] == x;} // ? Splay
    void prorev(int x) {swap(lc, rc), rev[x] ^= 1;} // ?????? (????) 
    void pushup(int x) {sumv[x] = sumv[lc] + sumv[rc] + 1;} //????
    void pushdown(int x) { //????
        if (rev[x]) {
            if (lc) prorev(lc);
            if (rc) prorev(rc);
            rev[x] = 0;
        }
    }
    void rotate(int x) { // ? Splay
        pushdown(fa[x]), pushdown(x);
        int y = fa[x], z = fa[y], k = rel(x), w = ch[x][k ^ 1];
        if (!isRt(y)) ch[z][rel(y)] = x;
        fa[x] = z; // y ??? (z, y) ???,???? (??? Splay ???? 1) 
        ch[y][k] = w, fa[w] = y;
        ch[x][k ^ 1] = y, fa[y] = x;
        pushup(y), pushup(x);
    }
    void splay(int x) { // ? Splay
        top = 0;
        stk[++top] = x;
        for (int pos = x; !isRt(pos); pos = fa[pos]) stk[++top] = fa[pos];
        while (top) pushdown(stk[top--]); // ? x ?????????????? pushdown (??? Splay ???? 2) 
        //???1:???? isRt ???,????pushdown 
        while (!isRt(x)) { // (??? Splay ???? 3.1) 
            int y = fa[x];
            if (!isRt(y)) { // (??? Splay ???? 3.2) 
                if (rel(x) == rel(y)) rotate(y); else rotate(x);
            }
            rotate(x);
        }
        //pushup(x); ??????  
    }
    void access(int x) { // ????? x ???????????,??? x ???????????????? 
        for (int y = 0; x; y = x, x = fa[x]) 
            splay(x), rc = y, pushup(x);
    }
    void makeRt(int x) { // ?????? 
        access(x), splay(x);
        prorev(x);
    }
    int findRt(int x) { // ????? x ?? 
        access(x), splay(x);
        while (pushdown(x), lc) x = lc;
        splay(x); // splay ???? 
        return x;
    }
    void split(int x, int y) { // ???????? x-y 
        makeRt(x);
        access(y), splay(y); // splay ???? 
        //???2:?(x)?y??????,???? access ??,y ??????,????? 
    }
    void link(int x, int y) { // ?????? x-y 
        makeRt(x);
        if (findRt(y) != x) fa[x] = y;
    }
    void cut(int x, int y) { // ?????? x-y 
        makeRt(x);
        if (findRt(y) == x && fa[y] == x && !ch[y][0]) {
            fa[y] = rc = 0;
            pushup(x);
        }
    }

    void clean() {
        ms(ch, 0), ms(fa, 0), ms(sumv, 0), ms(stk, 0), ms(rev, 0);
    }
    int solve() {

        clean();
        scanf("%d", &n);
        for (int i = 1; i <= n; ++i) {
            scanf("%d", &ki[i]);
            if (i + ki[i] > n) link(i, n + 1); else link(i, i + ki[i]);
        }
        scanf("%d", &q);
        while (q--) {
            int ty; scanf("%d", &ty);
            if (ty == 1) {
                int i; scanf("%d", &i);
                ++i;
                split(i, n + 1);
                printf("%d\n", sumv[n + 1] - 1);
            } else {
                int i, k; scanf("%d%d", &i, &k);
                ++i;
                if (i + ki[i] > n) cut(i, n + 1); else cut(i, i + ki[i]);
                ki[i] = k;
                if (i + ki[i] > n) link(i, n + 1); else link(i, i + ki[i]);
            }
        }

        return 0;
    }
}
int main() {
    flyinthesky::solve();
    return 0;
}
------ 本文结束 ------