SDOI2013 森林

主席树启发式合并。

一开始使用树剖LCA没发现出问题了,后来拍了半天才发现,然后换成了倍增。

当然也可以用LCT维护LCA。

#include <assert.h>
#include <algorithm>
#include <iostream>
#include <vector>
using int_t = int;
using std::cin;
using std::cout;
using std::endl;
const int_t LARGE = 1e5;
void* allocate();
struct Node {
    int begin, end;
    Node *left = nullptr, *right = nullptr;
    int value;
    Node(int_t begin, int_t end) {
        this->begin = begin;
        this->end = end;
        value = 0;
    }
    int_t query(int_t begin, int_t end) {
        if (end < this->begin || begin > this->end) return 0;
        if (this->begin >= begin && this->end <= end) return this->value;
        return left->query(begin, end) + right->query(begin, end);
    }
    void maintain() {
        if (begin != end) {
            this->value = left->value + right->value;
        }
    }
    static Node* build(int begin, int end) {
        Node* node = new Node(begin, end);
        if (begin != end) {
            int mid = (begin + end) / 2;
            node->left = build(begin, mid);
            node->right = build(mid + 1, end);
        }
        return node;
    }
};
int kth(int k, Node* v1, Node* v2, Node* lca, Node* lcap) {
    if (v1->begin == v1->end) return v1->begin;
    int_t leftVal = v1->left->value + v2->left->value - lca->left->value -
                    lcap->left->value;
    if (k <= leftVal)
        return kth(k, v1->left, v2->left, lca->left, lcap->left);
    else
        return kth(k - leftVal, v1->right, v2->right, lca->right, lcap->right);
}
Node* buildNext(Node* base, int_t pos, int_t val = 1) {
    Node* next = new (allocate()) Node(*base);
    if (next->begin == next->end) {
        next->value += 1;
    } else {
        int_t mid = (base->begin + base->end) / 2;
        if (pos <= mid)
            next->left = buildNext(base->left, pos, val);
        else
            next->right = buildNext(base->right, pos, val);
        next->maintain();
    }
    return next;
}

int depth[LARGE + 1], parent[LARGE + 1];
int vals[LARGE + 1];
int n, m, t;
int jumpto[21][LARGE + 1];
Node* roots[LARGE + 1];
std::vector<int_t> graph[LARGE + 1];
const int_t SIZE = LARGE * 200;
char buf[SIZE * sizeof(Node)];
int_t used = 0;
void* allocate() {
    assert(used < SIZE);
    return buf + (used++) * sizeof(Node);
}

namespace UFS {
int_t size[LARGE + 1], parent[LARGE + 1];
void init() {
    for (int i = 1; i <= n; i++) {
        size[i] = 1;
        parent[i] = i;
    }
}
int query(int x) {
    while (parent[x] != x) x = parent[x];
    return x;
}
void merge(int a, int b) {
    if (size[a] > size[b]) std::swap(a, b);
    a = query(a);
    b = query(b);
    if (a == b) return;
    size[b] += size[a];
    parent[a] = b;
}
}  // namespace UFS

void DFS1(int_t vtx, Node* base, int_t from = -1) {
    roots[vtx] = buildNext(base, vals[vtx], 1);
    parent[vtx] = from;
    if (from <= 0)
        jumpto[0][vtx] = -1;
    else
        jumpto[0][vtx] = from;
    for (int_t to : graph[vtx]) {
        if (to != from) {
            depth[to] = depth[vtx] + 1;
            DFS1(to, roots[vtx], vtx);
        }
    }
}
void DFS2(int vtx, int from, int k) {
    if ((1 << k) > depth[vtx])
        jumpto[k][vtx] = -1;
    else {
        jumpto[k][vtx] = jumpto[k - 1][jumpto[k - 1][vtx]];
    }
    for (auto to : graph[vtx]) {
        if (to != from) DFS2(to, vtx, k);
    }
}
void link(int v1, int v2) {
    if (UFS::size[UFS::query(v1)] > UFS::size[UFS::query(v2)])
        std::swap(v1, v2);

    graph[v1].push_back(v2);
    graph[v2].push_back(v1);
    depth[v1] = depth[v2] + 1;

    DFS1(v1, roots[v2], v2);
    for (int i = 1; i <= 18; i++) DFS2(v1, v2, i);
    UFS::merge(v1, v2);

}
int queryLCA(int v1, int v2) {
    if (depth[v1] < depth[v2]) std::swap(v1, v2);
    int diff = depth[v1] - depth[v2];
    for (int i = 18; i >= 0; i--) {
        if (diff & (1 << i)) v1 = jumpto[i][v1];
    }
    if (v1 == v2) return v1;
    assert(depth[v1] == depth[v2]);
    for (int i = 18; i >= 0; i--) {
        if (jumpto[i][v1] != jumpto[i][v2]) {
            v1 = jumpto[i][v1];
            v2 = jumpto[i][v2];
        }
    }
    assert(parent[v1] == parent[v2]);
    return parent[v1];
}
std::vector<int> numbers;
int query(int v1, int v2, int k) {
    if (v1 > n || v2 > n) return 0;
    if (UFS::query(v1) != UFS::query(v2)) return 0;
    auto lca = queryLCA(v1, v2);

    if (parent[lca] == -1)
        return kth(k, roots[v1], roots[v2], roots[lca], roots[0]);
    return kth(k, roots[v1], roots[v2], roots[lca], roots[parent[lca]]);
}
int getRank(int x) {
    return std::lower_bound(numbers.begin(), numbers.end(), x) -
           numbers.begin() + 1;
}
void process() {
    used = 0;
    scanf("%d%d%d", &n, &m, &t);
    numbers.clear();
    for (int i = 1; i <= n; i++) {
        scanf("%d", &vals[i]);
        numbers.push_back(vals[i]);
    }
    UFS::init();
    std::sort(numbers.begin(), numbers.end());
    numbers.resize(std::unique(numbers.begin(), numbers.end()) -
                   numbers.begin());
    for (int i = 1; i <= n; i++) {
        vals[i] = getRank(vals[i]);
    }
    for (int i = 1; i <= m; i++) {
        int v1, v2;
        scanf("%d%d", &v1, &v2);
        graph[v1].push_back(v2);
        graph[v2].push_back(v1);
        UFS::merge(v1, v2);
    }
    for (int i = 1; i <= n; i++)
        if (roots[i] == nullptr) {
            DFS1(i, roots[0]);
            //枚举顺序写错了!!
            for (int j = 1; j <= 18; j++) DFS2(i, -1, j);
        }
    int lastans = 0;
    for (int i = 1; i <= t; i++) {
        char buf[5];
        scanf("%s", buf);
        if (buf[0] == 'L') {
            int v1, v2;
            scanf("%d%d", &v1, &v2);
            v1 ^= lastans;
            v2 ^= lastans;

            link(v1, v2);
        } else {
            int v1, v2, k;
            scanf("%d%d%d", &v1, &v2, &k);
            v1 ^= lastans;
            v2 ^= lastans;
            k ^= lastans;
            int_t result = query(v1, v2, k);
            if (result == 80001) {
                cout << 0 << endl;
                continue;
            }
            lastans = numbers[result - 1];
            printf("%d\n", lastans);
        }
    }
}
int main() {
    roots[0] = Node::build(1, 8e4 + 1);
    int T;
    cin >> T;
    process();
    return 0;
}

 

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理