zoukankan      html  css  js  c++  java
  • 「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;
    }
    
  • 相关阅读:
    stm32之watchdog
    stm32之PWM
    stm32之GPIO(二)
    JavaScript之怎样获取元素节点
    JavaScript之对象学习
    JavaScript之数组学习
    Jquery遍历数组之$.inArray()方法介绍
    Jquery 遍历数组之$().each方法与$.each()方法介绍
    C# 移位运算符
    tensorboard简单使用
  • 原文地址:https://www.cnblogs.com/hongzy/p/10375507.html
Copyright © 2011-2022 走看看