「Luogu P3302」[SDOI2013]森林

Posted hlw1

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了「Luogu P3302」[SDOI2013]森林相关的知识,希望对你有一定的参考价值。

给出一片森林,每个点有一个权值,要求支持动态连边,并回答任意两点间第 k 小权值,强制在线。((1le N,M,T le 8 imes 10^4))

Luogu

分析

求第 k 小权值,这个肯定是用主席树了,但连边该怎么办?LCT?可我不会。

我们可以用启发式合并的方法,连边也就是合并两棵树,我们每次将较小的树连到较大的树上去,更新信息就暴力 dfs 较小树中的每一个点就好了。

代码

#include <bits/stdc++.h>

#define N 80003
#define DEBUG puts("ok")
#define File(x) freopen(x".in","r",stdin);freopen(x".out","w",stdout)

using namespace std;

int gi() {
    int x = 0, f = 1; char c = getchar();
    for ( ; !isdigit(c); c = getchar()) if (c == '-') f = -1;
    for ( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
    return x * f;
}

int n, m, q, len, tot, num;
int val[N], hs[N];
int fa[N][16], dep[N], sz[N], rt[N];
int to[N << 4], nxt[N << 4], hd[N << 4], ecnt;
int tr[N << 1], L[N << 5], R[N << 5], cnt[N << 5];
bool vis[N];

int get(int k) { return lower_bound(hs + 1, hs + 1 + len, k) - hs; }

void insert(int u, int v) { to[++ecnt] = v, nxt[ecnt] = hd[u], hd[u] = ecnt; }

void modify(int lst, int &now, int l, int r, int k) {
    if (!now) now = ++tot;
    cnt[now] = cnt[lst] + 1;
    if (l == r) return;
    int mid = l + r >> 1;
    if (k <= mid) R[now] = R[lst], modify(L[lst], L[now], l, mid, k);
    else L[now] = L[lst], modify(R[lst], R[now], mid + 1, r, k);
}

void dfs(int u, int f, int root) {
    vis[u] = 1, fa[u][0] = f, dep[u] = dep[f] + 1, sz[root]++, rt[u] = root;
    modify(tr[f], tr[u], 1, len, get(val[u]));
    for (int i = 1; i <= 15; ++i) fa[u][i] = fa[fa[u][i - 1]][i - 1];
    for (int i = hd[u]; i; i = nxt[i]) {
        int v = to[i];
        if (v == f) continue;
        dfs(v, u, root);
    }
}

int LCA(int u, int v) {
    if (dep[u] < dep[v]) swap(u, v);
    for (int i = 15; i >= 0; --i)
        if (dep[fa[u][i]] >= dep[v]) u = fa[u][i];
    if (u == v) return u;
    for (int i = 15; i >= 0; --i)
        if (fa[u][i] != fa[v][i]) u = fa[u][i], v = fa[v][i];
    return fa[u][0];
}

int query(int u, int v, int w, int f, int l, int r, int k) {
    if (l == r) return l;
    int sum, mid = l + r >> 1;
    sum = cnt[L[u]] + cnt[L[v]] - cnt[L[w]] - cnt[L[f]];
    if (k <= sum) return query(L[u], L[v], L[w], L[f], l, mid, k);
    else return query(R[u], R[v], R[w], R[f], mid + 1, r, k - sum);
}

void merge(int u, int v) {
    insert(u, v), insert(v, u);
    int r1 = rt[u], r2 = rt[v];
    if (sz[r1] > sz[r2]) dfs(v, u, r1);
    else dfs(u, v, r2);
}

int main() {
    int T = gi();
    int u, v, k, lst = 0; char ch[2];
    n = gi(), m = gi(), q = gi();
    for (int i = 1; i <= n; ++i) val[i] = hs[i] = gi();
    sort(hs + 1, hs + 1 + n);
    len = unique(hs + 1, hs + 1 + n) - hs - 1;
    for (int i = 1; i <= m; ++i) {
        u = gi(), v = gi();
        insert(u, v), insert(v, u);
    }
    for (int i = 1; i <= n; ++i) if (!vis[i]) dfs(i, 0, i);
    for (int i = 1; i <= q; ++i) {
        scanf("%s", ch);
        u = gi() ^ lst, v = gi() ^ lst;
        if (ch[0] == 'Q') {
            k = gi() ^ lst;
            int w = LCA(u, v), f = fa[w][0];
            printf("%d
", lst = hs[query(tr[u], tr[v], tr[w], tr[f], 1, len, k)]);
        }
        else merge(u, v);
    }
    return 0;
}

以上是关于「Luogu P3302」[SDOI2013]森林的主要内容,如果未能解决你的问题,请参考以下文章

Luogu 3302 森林(树上维护主席树)

P3302 SDOI2013森林

P3302 [SDOI2013]森林

主席树启发式合并P3302[SDOI2013]森林

p3302 [SDOI2013]森林(树上主席树+启发式合并)

Luogu3302 [SDOI2013]森林