洛谷 P3203 [HNOI2010]弹飞绵羊 题解

一、题目:

洛谷原题

二、思路:

好消息:我改用Atom编辑器了。Atom用的真舒服。

坏消息:最近脑子有点不好使,这么简单的题目想复杂了。

言归正传。

其实看到这种每个点只有一个依赖关系的时候,就要想一想基环树或者是树。他俩的区别就在于有没有一个“”存在。

那么本题是有一个根的,这个根就是“被弹飞”。

具体来说就是:(我们采用编号\(1\sim n\),和题目有所不同)

  1. \(x+k\leq n\),则连一条边\((x,x+k)\)
  2. \(x+k>n\),则连一条边\((x,n+1)\)

其中\(n+1\)即为虚拟根,表示绵羊被弹飞。

那么题目中的修改操作就对应着树的删边和加边,所以要使用动态树这个数据结构。

查询操作即为从\(x\)\(n+1\)的路径上点的个数,再减一。

做完了。

三、我坏掉的脑子

我原本没想到建一个大的虚拟根,而是森林中的每棵树都建了一个虚拟根,这样,如果删除一个节点和它的对应根之间的边,再连接到一个新树上去的话,还必须修改它所在树的虚拟根。极其麻烦。

四:代码:

正常代码:

#include <iostream>
#include <cstdio>
#include <cstring>

using namespace std;

inline int read(void) {
    int x = 0, f = 1; char ch = getchar();
    while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
    while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
    return f * x;
}

const int maxn = 4e5 + 5;

int n, fa[maxn], son[maxn][2], siz[maxn];
int K[maxn];
bool tag[maxn];

inline bool get(int x) {
    return son[fa[x]][1] == x;
}

inline bool isroot(int x) {
    return son[fa[x]][0] != x && son[fa[x]][1] != x;
}

inline void pushup(int x) {
    siz[x] = siz[son[x][0]] + siz[son[x][1]] + 1;
}

inline void rev(int x) {
    tag[x] ^= 1;
    swap(son[x][0], son[x][1]);
}

inline void pushdown(int x) {
    if (tag[x]) {
        if (son[x][0]) rev(son[x][0]);
        if (son[x][1]) rev(son[x][1]);
        tag[x] = 0;
    }
}

inline void rotate(int x) {
    int y = fa[x], z = fa[y], k = get(x);
    if (z && !isroot(y)) son[z][get(y)] = x;
    son[y][k] = son[x][k ^ 1]; fa[son[y][k]] = y; fa[y] = x;
    son[x][k ^ 1] = y; fa[x] = z;
    pushup(y); pushup(x);
}

inline void correct(int x) {
    if (!isroot(x)) correct(fa[x]);
    pushdown(x);
}

inline void splay(int x) {
    correct(x);
    for (int f = fa[x]; !isroot(x); rotate(x), f = fa[x])
        if (!isroot(f))
            rotate(get(x) == get(f) ? f : x);
}

inline void access(int x) {
    int z = x;
    for (int y = 0; x; y = x, x = fa[x]) {
        splay(x);
        son[x][1] = y;
        pushup(x);
    }
    splay(z);
}

inline int findroot(int x) {
    access(x);
    while (son[x][0])
        pushdown(x), x = son[x][0];
    splay(x);
    return x;
}

inline void makeroot(int x) {
    access(x);
    rev(x);
}

inline void split(int x, int y) {
    makeroot(x);
    access(y);
}

inline void link(int x, int y) {
    makeroot(x);
    if (findroot(y) != x) {
        fa[x] = y;
    }
}

inline void cut(int x, int y) {
    split(x, y);
    if (son[y][0] == x && !son[x][1]) {
        son[y][0] = 0; fa[x] = 0;
        pushup(y);
    }
}

int main() {
    n = read();
    for (int i = 1; i <= n; ++ i) {
        K[i] = read();
        if (i + K[i] > n) {
            link(i, n + 1);
        }
        else link(i, i + K[i]);
    }
    int m = read();
    while (m --) {
        int opt = read();
        if (opt == 1) {
            int x = read();
            ++ x;
            split(x, n + 1);
            printf("%d\n", siz[n + 1] - 1);
        } else {
            int x = read(), upd = read();
            ++ x;
            if (x + K[x] > n) {
                cut(x, n + 1);
            }
            else cut(x, x + K[x]);

            K[x] = upd;
            if (x + K[x] > n) {
                link(x, n + 1);
            }
            else link(x, x + K[x]);
        }
    }
    return 0;
}

坏掉脑子的写法(已AC)

#include <iostream>
#include <cstdio>
#include <cstring>

using namespace std;

inline int read(void) {
    int x = 0, f = 1; char ch = getchar();
    while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
    while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
    return f * x;
}

const int maxn = 4e5 + 5;

int n, fa[maxn], son[maxn][2], siz[maxn], root[maxn];
int K[maxn];
bool tag[maxn];

inline bool get(int x) {
    return son[fa[x]][1] == x;
}

inline bool isroot(int x) {
    return son[fa[x]][0] != x && son[fa[x]][1] != x;
}

inline void pushup(int x) {
    siz[x] = siz[son[x][0]] + siz[son[x][1]] + 1;
}

inline void rev(int x) {
    tag[x] ^= 1;
    swap(son[x][0], son[x][1]);
}

inline void pushdown(int x) {
    if (tag[x]) {
        if (son[x][0]) rev(son[x][0]);
        if (son[x][1]) rev(son[x][1]);
        tag[x] = 0;
    }
}

inline void rotate(int x) {
    int y = fa[x], z = fa[y], k = get(x);
    if (z && !isroot(y)) son[z][get(y)] = x;
    son[y][k] = son[x][k ^ 1]; fa[son[y][k]] = y; fa[y] = x;
    son[x][k ^ 1] = y; fa[x] = z;
    pushup(y); pushup(x);
}

inline void correct(int x) {
    if (!isroot(x)) correct(fa[x]);
    pushdown(x);
}

inline void splay(int x) {
    correct(x);
    for (int f = fa[x]; !isroot(x); rotate(x), f = fa[x])
        if (!isroot(f))
            rotate(get(x) == get(f) ? f : x);
}

inline void access(int x) {
    int z = x;
    for (int y = 0; x; y = x, x = fa[x]) {
        splay(x);
        son[x][1] = y;
        pushup(x);
    }
    splay(z);
}

inline int findroot(int x) {
    access(x);
    while (son[x][0])
        pushdown(x), x = son[x][0];
    splay(x);
    return x;
}

inline void makeroot(int x) {
    int tmp = root[findroot(x)];
    access(x);
    root[x] = tmp;
    rev(x);
}

inline void split(int x, int y) {
    makeroot(x);
    access(y);
}

inline void link(int x, int y) {
    makeroot(x);
    if (findroot(y) != x) {
        fa[x] = y;
    }
}

inline void cut(int x, int y) {
    int tmp = root[findroot(x)];
    split(x, y);
    if (son[y][0] == x && !son[x][1]) {
        son[y][0] = 0; fa[x] = 0;
        pushup(y);
        root[findroot(y)] = tmp;
    }
}

int main() {
    n = read();
    int r = n;
    for (int i = 1; i <= n; ++ i) {
        K[i] = read();
        if (i + K[i] > n) {
            link(i, ++ r);
            root[findroot(i)] = r;
        }
        else link(i, i + K[i]);
    }
    int m = read();
    while (m --) {
        int opt = read();
        if (opt == 1) {
            int x = read();
            ++ x;
            int tmp = root[findroot(x)];
            split(x, tmp);
            printf("%d\n", siz[tmp] - 1);
        } else {
            int x = read(), upd = read();
            ++ x;
            if (x + K[x] > n) {
                int tmp = root[findroot(x)];
                cut(x, tmp);
            }
            else cut(x, x + K[x]);

            K[x] = upd;
            if (x + K[x] > n) {
                link(x, ++ r);
                root[findroot(x)] = r;
            }
            else link(x, x + K[x]);
        }
    }
    return 0;
}

posted @ 2021-02-20 22:39  蓝田日暖玉生烟  阅读(95)  评论(0编辑  收藏  举报