• AcWing 1063. 永无乡(并查集, 启发式合并,splay)


    题目链接

    解题思路

      题目相当于询问每个点所属集合的第(k)小的数的编号,查询某个数属于哪个集合可以用并查集来解决。题目还涉及了集合之间的合并,由于合并之后集合的元素会改变,所以需要动态询问第(k)小,可以用平衡树来解决。另外,在集合合并的时候需要用启发式合并,把小集合合并到大集合上,这样可以把时间复杂度降到(nlog^2n)的级别。

    const int maxn = 1e5+10;
    int n, m, a[maxn], p[maxn];
    int find(int x) {
        return p[x]==x ? p[x]:p[x]=find(p[x]);
    }
    int rt[maxn], idx;
    struct Node {
        int s[2], sz, v, p, num;
        void init(int _v, int _num, int _p) {
            v = _v, num = _num, p = _p;
            sz = 1;
        }
    } tr[maxn*40];
    inline void push_up(int u) {
        tr[u].sz = tr[tr[u].s[0]].sz+tr[tr[u].s[1]].sz+1;
    }
    void rotate(int x) {
        int y = tr[x].p, z = tr[y].p;
        int k = tr[y].s[1]==x;
        tr[z].s[tr[z].s[1]==y] = x, tr[x].p = z;
        tr[y].s[k] = tr[x].s[k^1], tr[tr[x].s[k^1]].p = y;
        tr[x].s[k^1] = y, tr[y].p = x;
        push_up(y), push_up(x);
    }
    void splay(int &rt,  int x, int k) {
        while(tr[x].p!=k) {
            int y = tr[x].p, z = tr[y].p;
            if (z!=k) 
                if ((tr[y].s[1]==x)^(tr[z].s[1]==y)) rotate(x);
                else rotate(y);
            rotate(x);
        }
        if (!k) rt = x;
    }
    void insert(int &rt, int v, int num) {
        int p = 0, u = rt;
        while(u) p = u, u = tr[u].s[v>tr[u].v];
        u = ++idx;
        if (p) tr[p].s[v>tr[p].v] = u;
        tr[u].init(v, num, p);
        splay(rt, u, 0);
    }
    void merge(int u, int &rt) {
        if (tr[u].v==INF || tr[u].v==-INF) return;
        insert(rt, tr[u].v, tr[u].num);
        if (tr[u].s[0]) merge(tr[u].s[0], rt);
        if (tr[u].s[1]) merge(tr[u].s[1], rt);
    }
    int query(int u, int k) {
        while(u) {
            int sz = tr[tr[u].s[0]].sz;
            if (sz>=k) u = tr[u].s[0];
            else if (sz+1==k) return tr[u].num;
            else k -= sz+1, u = tr[u].s[1];
        }
        return -1;
    }
    int main() {
        IOS;
        cin >> n >> m;
        for (int i = 1; i<=n; ++i) cin >> a[i];
        for (int i = 1; i<=n; ++i) p[i] = i;
        while(m--) {
            int a, b; cin >> a >> b;
            p[find(a)] = find(b);
        }
        for (int i = 1; i<=n; ++i) {
            int pi = find(i);
            if (!rt[pi]) {
                insert(rt[pi], -INF, i);
                insert(rt[pi], INF, i);
            }
            insert(rt[pi], a[i], i);
        }
        int q; cin >> q;
        while(q--) {
            char op[10]; int a, b;
            cin >> op >> a >> b;
            if (op[0]=='B') {
                int fa = find(a);
                int fb = find(b);
                if (fa!=fb) {
                    if (tr[rt[fa]].sz<=tr[rt[fb]].sz) {
                        p[fa] = fb;
                        merge(rt[fa], rt[fb]);
                    }
                    else {
                        p[fb] = fa;
                        merge(rt[fb], rt[fa]);
                    }
                }
            }
            else {
                int fa = find(a);
                if (tr[rt[fa]].sz-2<b) cout << -1 << endl;
                else cout << query(rt[fa], b+1) << endl;
            }
        }
        return 0;
    }
    
  • 相关阅读:
    ZOJ 3349 Special Subsequence
    ZOJ 3684 Destroy
    ZOJ 3820 Building Fire Stations
    HDU 5291 Candy Distribution
    HDU 3639 Hawk-and-Chicken
    HDU 4780 Candy Factory
    HDU 4276 The Ghost Blows Light
    ZOJ 3556 How Many Sets I
    技术人员的眼界问题
    神经网络和深度学习
  • 原文地址:https://www.cnblogs.com/shuitiangong/p/15088830.html
Copyright © 2020-2023  润新知