SPOJ OTOCI (动态树)

题意:有n个节点,每个节点有一个权值,现有以下三种操作:

1、bridge A B 如果节点A、B连通输出no;否则输出yes,并在A、B之间连一条边;

2、penguins A X 将节点A的权值修改为X;

3、excursion A B 如果节点A、B不连通输出impossible;否则输出节点A到B的路径上的权值和。


思路:动态树模板题,1操作是动态树的删边操作,2操作是简单的修改,不过得先把节点提伸至根节点,3操作可以先把A节点成为该点所在树的根节点,然后B节点与根节点相连,然后再splay维护一下权值和即可。


#include
#include
#include
const int maxn = 3 * 1e4 + 10;
using namespace std;

struct link_cut {
private:
    struct Node {
        int idx, isrev, tal;
        int fa, ch[2], path_pre;
    };
    Node node[maxn];
    int nodecnt, loc[maxn];
    int data[maxn];
    void update(int x) {
        int &ls = node[x].ch[0];
        int &rs = node[x].ch[1];
        int &id = node[x].idx;
        node[x].tal = node[ls].tal + node[rs].tal + data[id];
    }
    ///使树的形态正常化
    void nor(int p) {
        if(node[p].isrev) {
            node[p].isrev = 0;
            int &l = node[p].ch[0];
            int &r = node[p].ch[1];
            node[l].isrev ^= 1;
            node[r].isrev ^= 1;
            swap(l, r);
        }
        update(p);
    }
    ///旋转
    void __rotate(int x, int d) {
        int y = node[x].fa;
        node[x].path_pre = node[y].path_pre;
        node[y].path_pre = 0;
        node[y].ch[d ^ 1] = node[x].ch[d];
        if(node[x].ch[d]) node[node[x].ch[d]].fa = y;
        node[x].fa = node[y].fa;
        if(node[y].fa) {
            if(y == node[node[y].fa].ch[0])
                node[node[y].fa].ch[0] = x;
            else node[node[y].fa].ch[1] = x;
        }
        node[y].fa = x; node[x].ch[d] = y;
        update(y); update(x);
    }
    ///提伸x为该Spaly的根节点
    void splay(int x) {
        while(node[x].fa) {
            int fa = node[x].fa;
            nor(node[x].fa);
            if(node[fa].ch[0]) nor(node[fa].ch[0]);
            if(node[fa].ch[1]) nor(node[fa].ch[1]);
            if(x == node[fa].ch[0]) __rotate(x, 1);
            else __rotate(x, 0);
        }
    }
    ///将节点v通过实路径连接至根节点
    void access(int p) {
        splay(p); nor(p);
        int q = node[p].ch[1];
        node[p].ch[1] = node[q].fa = 0; ///断裂
        node[q].path_pre = p;
        for(q = node[p].path_pre; q; q = node[p].path_pre) {
            splay(q); nor(q);
            int r = node[q].ch[1];
            node[r].fa = node[p].path_pre = 0;
            node[r].path_pre = node[p].fa = q;
            node[q].ch[1] = p; p = q;
        }
        splay(p);
    }
    ///找p的根节点
    int find_root(int p) {
        access(p); splay(p); nor(p);
        while(node[p].ch[0]) p = node[p].ch[0];
        splay(p);
        return p;
    }
    ///将p到根节点上的所有边取反
    ///即p成为根节点
    void evert(int p) {
        access(p); splay(p);
        node[p].isrev ^= 1;
        nor(p);
    }
    ///将边(u, v)断开
    void cut(int p, int q) {
        evert(p); access(q);
        splay(q); nor(q);
        node[node[q].ch[0]].fa = 0;
        node[q].ch[0] = 0;
        nor(q);
    }
    ///连接边(u, v)
    void link(int p, int q) {
        evert(p); splay(p);
        nor(p); access(q);
        splay(q); nor(q);
        node[p].ch[0] = q;
        node[q].fa = p;
        nor(p);
    }
    ///修改权值
    void change(int p, int d, int pre) {
        splay(p);
        node[p].tal += d - pre;
    }
    ///p节点到q节点上的路径权值和
    int sum(int p, int q) {
        evert(p); splay(p);
        nor(p);   access(q);
        splay(q); nor(q);
        return node[q].tal;
    }
public:
    void init() {
        nodecnt = node[0].tal = 0;
        memset(loc, 0, sizeof loc);
        memset(data, 0, sizeof data);
    }
    ///创建一颗只有一个节点的Splay
    void make_tree(int id) {
        int p = ++nodecnt;
        loc[id] = p; node[p].path_pre = 0;
        node[p].fa = node[p].ch[0] = node[p].ch[1] = 0;
        node[p].idx = id;
    }
    ///用loc数组是因为点标号和节点标号可能不一样
    int getroot(int id) { return node[find_root(loc[id])].idx; }
    void add(int x, int y) { link(loc[x], loc[y]); }
    void destroy(int x, int y) { cut(loc[x], loc[y]); }
    void Change(int x, int d) { change(loc[x], d, data[x]); data[x] = d; }
    int getsum(int x, int y) { return sum(loc[x], loc[y]); }
} tree;
int n, w, q, x, y;
char s[20];

int main() {
    while(scanf("%d", &n) != EOF) {
        tree.init();
        for(int i = 1; i <= n; i++)
            tree.make_tree(i);
        for(int i = 1; i <= n; i++) {
            scanf("%d", &w);
            tree.Change(i, w);
        }
        scanf("%d", &q);
        while(q--) {
            scanf("%s %d %d", s, &x, &y);
            if(s[0] == 'p') tree.Change(x, y);
            else if(s[0] == 'b') {
                int nx = tree.getroot(x);
                int ny = tree.getroot(y);
                if(nx == ny) printf("no\n");
                else {
                    printf("yes\n");
                    tree.add(x, y);
                }
            } else {
                int nx = tree.getroot(x);
                int ny = tree.getroot(y);
                if(nx != ny) printf("impossible\n");
                else printf("%d\n", tree.getsum(x, y));
            }
        }
    }
    return 0;
}

你可能感兴趣的:(数据结构)