[HNOI2012]永无乡 线段树合并

[HNOI2012]永无乡

LG传送门

线段树合并练手题,写这篇博客只是为了给我的这篇文章找个板子题。

并查集维护连通性,对于不在同一个连通块内的合并操作每次直接合并两颗线段树,复杂度\(O(n \log n)\)

//written by newbiechd
#include <cstdio>
#define R register
#define I inline
using namespace std;
const int N = 100003;
int f[N], id[N], rt[N], T;
struct segtree {
    int p, q, s;
}e[N << 5];
I int find(int x) {
    R int r = x, y;
    while (f[r] ^ r)
        r = f[r];
    while (x ^ r)
        y = f[x], f[x] = r, x = y;
    return r;
}
void insert(int &k, int l, int r, int x) {
    k = ++T, ++e[k].s;
    if (l == r)
        return ;
    R int m = (l + r) >> 1;
    if (x <= m)
        insert(e[k].p, l, m, x);
    else
        insert(e[k].q, m + 1, r, x);
}
int merge(int k, int t, int l, int r) {
    if (!k)
        return t;
    if (!t)
        return k;
    e[k].s += e[t].s;
    if (l == r)
        return k;
    R int m = (l + r) >> 1;
    e[k].p = merge(e[k].p, e[t].p, l, m),
        e[k].q = merge(e[k].q, e[t].q, m + 1, r);
    return k;
}
int query(int k, int l, int r, int x) {
    if (l == r)
        return l;
    R int m = (l + r) >> 1, t = e[e[k].p].s;
    if (x <= t)
        return query(e[k].p, l, m, x);
    else
        return query(e[k].q, m + 1, r, x - t);
}
int main() {
    R int n, m, Q, i, x, y, z;
    R char opt[2];
    scanf("%d%d", &n, &m);
    for (i = 1; i <= n; ++i)
        f[i] = i, scanf("%d", &z), id[z] = i, insert(rt[i], 1, n, z);
    for (i = 1; i <= m; ++i)
        scanf("%d%d", &x, &y), x = find(x), y = find(y), f[y] = x,
            rt[x] = merge(rt[x], rt[y], 1, n);
    scanf("%d", &Q);
    for (i = 1; i <= Q; ++i) {
        scanf("%s%d%d", opt, &x, &y), x = find(x);
        if (opt[0] == 'B') {
            y = find(y);
            if (y ^ x)
                f[y] = x, merge(rt[x], rt[y], 1, n);
        }
        else
            if (y > e[rt[x]].s)
                printf("-1\n");
            else
                printf("%d\n", id[query(rt[x], 1, n, y)]);
    }
    return 0;
}

转载于:https://www.cnblogs.com/cj-chd/p/10433154.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值