【题目描述】

永无乡包含 n 座岛,编号从 1 到 n,每座岛都有自己的独一无二的重要度,按照重要度可 以将这 n 座岛排名,名次用 1 到 n 来表示。

某些岛之间由巨大的桥连接,通过桥可以从一个岛 到达另一个岛。如果从岛 a 出发经过若干座(含 0 座)桥可以到达岛 b,则称岛 a 和岛 b 是连 通的。

现在有两种操作:

  • B x y 表示在岛 x 与岛 y 之间修建一座新桥。

  • Q x k 表示询问当前与岛 x 连通的所有岛中第 k 重要的是哪座岛,即所有与岛 x 连通的岛中重要度排名第 k 小的岛是哪 座,请你输出那个岛的编号。

【题目链接】

BZOJ 2377 永无乡 【HNOI2012】

【解题思路】

连通性可以用并查集维护。

查第 k 小可以用某些数据结构维护,这里我选择了平衡树。

现在的主要问题是,当我们合并两个集合时,如何快速合并对应的平衡树。

然而并不需要什么快速合并,我们每次直接把小的那颗暴力合并到大的那颗上即可。

复杂度保证?每个元素被转移后,所在树大小至少翻倍,故每个元素至多被转移 $O(logn)$ 次,每次转移是 $O(logn)$ 的,所以复杂度至多是 $O(nlog^2n)$的。

【AC代码】

平衡树写了旋转式 Treap

注意数据有坑,有一行为 0 0,属于非法数据,忽略即可。

#include <cstdio>
#include <vector>
#include <cstdlib>
#include <queue>
 
typedef int Rel;
const int L = 0, R = 1;
 
template <typename Item>
struct Treap{
    struct Node{
        Item value;
        int fix;
        Node *child[2];
 
        int size;
 
        Node(const Item &x){
            value = x;
            child[L] = child[R] = NULL;
            fix = rand();
            size = 1;
        }
 
        ~Node(){
            if(child[L]) delete child[L];
            if(child[R]) delete child[R];
        }
 
        inline int lsize(){
            return child[L] ? child[L]->size : 0;
        }
 
        inline int rsize(){
            return child[R] ? child[R]->size : 0;
        }
 
        inline void update(){
            size = lsize() + rsize() + 1;
        }
 
        inline Rel comp(const Item &x){
            return x < value ? L : R;
        }
    } *root;
 
    Treap(){
        root = NULL;
        srand(19991205);
    }
 
    ~Treap(){
        if(root) delete root;
    }
 
    void rotate(Node *&v, Rel d){
        Node *k = v->child[d ^ 1];
        v->child[d ^ 1] = k->child[d];
        if(k) k->child[d] = v;
        v->update(), k->update();
        v = k;
    }
 
    void insert(Node *& v, Node *node){
        if(!v) v = node;
        else{
            Rel d = v->comp(node->value);
            insert(v->child[d], node);
            if(v->child[d]->fix < v->fix) rotate(v, d ^ 1);   
        } 
        v->update();
    }
 
    Item select(int k){
        Node *v = root;
        while(5261){
            if(k == v->lsize()) return v->value;
            else if(k < v->lsize()) v = v->child[L];
            else k -= v->lsize() + 1, v = v->child[R];
        }
        return v->value;
    }
 
    void insert(const Item &x){
        insert(root, new Node(x));
    }
 
    void insert(Node *node){
        insert(root, node);
    }
 
    int size(){
        return root->size;
    }
 
    void fetch(std::vector<Node*> &nodes){
        nodes.reserve(size());
 
        std::queue<Node*> Q;
        Q.push(root);
        while(!Q.empty()){
            Node *v = Q.front(); Q.pop();
            nodes.push_back(v);
            if(v->child[L]) Q.push(v->child[L]);
            if(v->child[R]) Q.push(v->child[R]);
        }
    }
 
    void mergeWith(Treap *T){
        std::vector<Node*> nodes;
        T->fetch(nodes);
        for(int i = 0; i < nodes.size(); i++){
            insert(nodes[i]);
        }
    }
};
 
const int MAXN = 100000;
 
struct NeverLand{
    struct Info{
        int id, value;
 
        Info() {}
        Info(const int id, const int value) : id(id), value(value) {}
 
        bool operator<(const Info &another) const{
            return value < another.value;
        }
    };
 
    struct Land{
        Land *cheif;
 
        Treap<Info> *T;
 
        Land() : cheif(this), T(new Treap<Info>) {}
 
        ~Land(){
            delete T;
        }
    } lands[MAXN];
 
    void build(int a[], int n){
        for(int i = 0; i < n; i++){
            Land *v = lands + i;
 
            (lands + i)->T->insert(Info(i, a[i]));
        }
    }
 
    Land *find(Land *v){
        return v == v->cheif ? v : v->cheif = find(v->cheif);
    }
 
    void merge(int a, int b){
        if(a < 0 || b < 0) return;
 
        Land *x = find(lands + a), *y = find(lands + b);
        if(x->T->size() < y->T->size()) std::swap(x, y);
        if(x != y){
            y->cheif = x;
 
            x->T->mergeWith(y->T);
            y->T = NULL;
        }
    }
 
    int query(int x, int k){
        Treap<Info> *T = find(lands + x)->T;
 
        if(k < 0 || k >= T->size()) return -1;
        else return T->select(k).id;
    }
 
} neverLand;
 
int a[MAXN];
 
int main(){
    freopen("data.in", "r", stdin), freopen("bzoj_2733.out", "w", stdout);
 
    int n, m;
 
    scanf("%d%d", &n, &m);
    for(int i = 0; i < n; i++){
        scanf("%d", a + i);
    }
    neverLand.build(a, n);
    for(int i = 0; i < m; i++){
        int a, b;
        scanf("%d%d", &a, &b);
        a--, b--;
 
        neverLand.merge(a, b);
    }
 
    int q;
    scanf("%d", &q);
    for(int i = 0; i < q; i++){
        char opt;
        do opt = getchar(); while(opt != 'Q' && opt != 'B');
 
        if(opt == 'Q'){
            int x, k;
            scanf("%d%d", &x, &k);
            x--, k--;
 
            int ans = neverLand.query(x, k);
 
            if(ans != -1) printf("%d\n", ans + 1);
            else puts("-1");
 
        } else if(opt == 'B'){
            int a, b;
            scanf("%d%d", &a, &b);
            a--, b--;
 
            neverLand.merge(a, b);
        } else throw;
    }
 
    fclose(stdin), fclose(stdout);
    return 0;
}