题目链接:SPOJ 913

给定一棵 $n$ 个节点的树,第 $i$ 条边有边权 $c_i$,需要支持如下 $2$ 种操作:

  • DIST a b:询问点 $a$ 和 $b$ 之间的边权和。
  • KTH a b k:询问点 $a$ 到 $b$ 的有向路径的第 $k$ 个点的标号。

询问以 DONE 结束。

本题有 $T$ 组数据。

数据范围:$1\le T\le 25$,$1\le n\le 10 ^ 4$,$1\le c_i\le 10 ^ 5$。


Solution

这题还是直接将边化为点,主要问题是 KTH 操作:由于边变成了点,因此我们要查询的是 $a$ 和 $b$ 组成的 $\text{Splay}$ 中的第 $2k - 1$ 个点。直接套用 $\text{Splay}$ 中的 kth 函数即可。

时间复杂度:$\mathcal O(T \cdot n\log n)$。


Code

#include <cstdio>
#include <algorithm>

const int N = 2e4 + 5;

int n;

struct Node;
Node *null;

struct Node {
    Node *ch[2], *fa;
    int val, sz, sum, idx;
    bool rev;
    Node() {
        ch[0] = ch[1] = fa = null;
        val = sz = sum = idx = rev = 0;
    }
    bool get() {
        return fa->ch[1] == this;
    }
    bool isroot() {
        return fa->ch[0] != this && fa->ch[1] != this;
    }
    void reverse() {
        rev ^= 1, std::swap(ch[0], ch[1]);
    }
    void pushup() {
        sz = ch[0]->sz + ch[1]->sz + 1;
        sum = ch[0]->sum + ch[1]->sum + val;
    }
    void pushdown() {
        if (rev) {
            ch[0]->reverse();
            ch[1]->reverse();
            rev = 0;
        }
    }
} *a[N];

struct LCT {
    LCT() {
        null = new Node();
        null->ch[0] = null->ch[1] = null->fa = null;
        null->sz = 0;
    }
    void build(int n) {
        for (int i = 1; i <= n; i++) {
            a[i] = new Node();
        }
    }
    void clear(int n) {
        delete null;
        for (int i = 1; i <= n; i++) {
            delete a[i];
        }
    }
    void pushtag(Node *x) {
        if (!x->isroot()) pushtag(x->fa);
        x->pushdown();
    }
    void rotate(Node *x) {
        Node *y = x->fa, *z = y->fa;
        int k = x->get();
        !y->isroot() && (z->ch[y->get()] = x), x->fa = z;
        y->ch[k] = x->ch[!k], x->ch[!k]->fa = y;
        x->ch[!k] = y, y->fa = x;
        y->pushup();
    }
    void splay(Node *x) {
        pushtag(x);
        while (!x->isroot()) {
            Node *y = x->fa;
            if (!y->isroot()) {
                rotate(x->get() == y->get() ? y : x);
            }
            rotate(x);
        }
        x->pushup();
    }
    Node *kth(Node *rt, int k) {
        Node *u = rt;
        while (1) {
            u->pushdown();
            if (k <= u->ch[0]->sz) {
                u = u->ch[0];
            } else if (k > u->ch[0]->sz + 1) {
                k -= u->ch[0]->sz + 1;
                u = u->ch[1];
            } else {
                return u;
            }
        }
    }
    void access(Node *x) {
        for (Node *y = null; x != null; x = (y = x)->fa) {
            splay(x), x->ch[1] = y, x->pushup();
        }
    }
    void makeroot(Node *x) {
        access(x), splay(x), x->reverse();
    }
    void split(Node *x, Node *y) {
        makeroot(x), access(y), splay(y);
    }
    void link(Node *x, Node *y) {
        makeroot(x);
        x->fa = y;
    }
};

int main() {
    int T;
    for (scanf("%d", &T); T--; ) {
        scanf("%d", &n);
        LCT T;
        T.build(n + n - 1);
        for (int i = 1; i <= n; i++) {
            a[i]->idx = i;
        }
        for (int i = 1; i < n; i++) {
            int u, v, w;
            scanf("%d%d%d", &u, &v, &w);
            a[i + n]->val = w;
            T.link(a[u], a[i + n]);
            T.link(a[v], a[i + n]);
        }
        while (1) {
            char s[10];
            scanf("%s", s + 1);
            if (s[2] == 'O') break;
            if (s[1] == 'D') {
                int u, v;
                scanf("%d%d", &u, &v);
                T.split(a[u], a[v]);
                printf("%d\n", a[v]->sum);
            } else {
                int u, v, k;
                scanf("%d%d%d", &u, &v, &k);
                T.split(a[u], a[v]);
                printf("%d\n", T.kth(a[v], k + k - 1)->idx);
            }
        }
        T.clear(n + n - 1);
    }
    return 0;
}
最后修改:2019 年 06 月 28 日
如果觉得我的文章对你有用,请随意赞赏