• 「BZOJ 2733」「HNOI 2012」永无乡「启发式合并」


    题意

    你需要维护若干连通快,有两个操作

    • 合并(x,y)所在的连通块
    • 询问(x)所在连通块中权值从小到大排第(k)的结点编号

    题解

    可以启发式合并(splay),感觉比较好些的

    一个连通块就是一个(splay),每次合并挑小的(splay)遍历一遍把点按中序遍历存下来,然后一个一个插入大的(splay)就行了;查询就是(splay)(kth)操作

    这样时间复杂度(O(n log n)),它的证明可以见2018论文集 :董炜隽《浅谈Splay与Treap的性质及其应用》,其中有提一个( ext{Dynamic Finger Theorem})

    (其实随便插入的话是两个(log),也能通过,十分玄学)

    #include <algorithm>
    #include <cstdio>
    #include <vector>
    using namespace std;
    
    const int N = 2e5 + 10;
    
    int n, m, q, bel[N], rt[N], w[N];
    int ch[N][2], fa[N], sz[N];
    
    void upd(int u) { sz[u] = sz[ch[u][0]] + sz[ch[u][1]] + 1; }
    int dir(int u) { return ch[fa[u]][1] == u; }
    
    void rotate(int u) {
        int d = dir(u), f = fa[u];
        if(fa[u] = fa[f]) ch[fa[u]][dir(f)] = u;
        if(ch[f][d] = ch[u][d ^ 1]) fa[ch[f][d]] = f;
        fa[ch[u][d ^ 1] = f] = u;
        upd(f); upd(u);
    }
    void ins(int &rt, int u, int f = 0) {
        if(!rt) {
            rt = u; fa[u] = f; sz[u] = 1;
            ch[u][0] = ch[u][1] = 0;
            return ;
        }
        ins(ch[rt][w[u] > w[rt]], u, rt);
        upd(rt);
    }
    void splay(int u) {
        for(; fa[u]; rotate(u)) if(fa[fa[u]])
            rotate(dir(u) == dir(fa[u]) ? fa[u] : u);
        rt[bel[u]] = u;
    }
    int kth(int u, int k) {
        int v = u;
        while(1) {
            if(k <= sz[ch[v][0]]) v = ch[v][0];
            else {
                k -= sz[ch[v][0]] + 1;
                if(k <= 0) break ;
                v = ch[v][1];
            }
        }
        splay(v);
        return v;
    }
    
    int a[N], l;
    void dfs(int u) {
        if(u) {
            dfs(ch[u][0]);
            a[++ l] = u;
            dfs(ch[u][1]);
        }
    }
    
    void link(int u, int v) {
        u = bel[u]; v = bel[v];
        if(u == v) return ;
        if(sz[rt[u]] < sz[rt[v]]) swap(u, v);
        l = 0; dfs(rt[v]);
        for(int i = 1; i <= l; i ++) {
            bel[a[i]] = u;
            ins(rt[u], a[i]);
            splay(a[i]);
        }
    }
    
    int main() {
        scanf("%d%d", &n, &m);
        for(int i = 1; i <= n; i ++) scanf("%d", w + i);
        for(int i = 1; i <= n; i ++) {
            bel[i] = rt[i] = i; sz[i] = 1;
        }
        int u, v;
        for(int i = 1; i <= m; i ++) {
            scanf("%d%d", &u, &v);
            link(u, v);
        }
        scanf("%d", &q);
        char op[5];
        for(int i = 1; i <= q; i ++) {
            scanf("%s%d%d", op, &u, &v);
            if(* op == 'Q') {
                u = rt[bel[u]];
                if(v > sz[u]) puts("-1");
                else printf("%d
    ", kth(u, v));
            }
            if(* op == 'B') link(u, v);
        }
        return 0;
    }
    
  • 相关阅读:
    hdu--1018--Big Number(斯特林公式)
    NYOJ--56--阶乘因式分解(一)
    hdu--1711--kmp应用在整形数组--Number Sequence
    HDU--1358--KMP算法失配函数getfail()的理解--Period
    C++STL(vector,map,set,list,bitset,deque)成员函数整理
    NYOJ--95--multiset--众数问题
    NYOJ--86--set.find()--找球号(一)
    NYOJ--19--next_permutation()--擅长排列的小明
    NYOJ--714--Card Trick
    NYOJ--2--括号配对问题
  • 原文地址:https://www.cnblogs.com/hongzy/p/10375507.html
Copyright © 2020-2023  润新知