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

Posted kaka0010

tags:

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

原题链接:https://www.luogu.com.cn/problem/P3302

题意

一个森林由n个节点m条边组成,满足下列两种操作

  1. Q x y k 查询x到y路径上权值第k小的点
  2. L x y 将x和y之间连一条边

注意本题强制在线

分析

看到动态建边操作,很多人会想到LCT,这题应该是可以写的,但我不会

其实这题就是P2633 Count on a tree的加强版,多了一个动态建边的过程。在那道题中,求树上路径的第k小点时,左子树的大小可以转化为
s i z [ u ] + s i z [ v ] − s i z [ l c a ] − s i z [ f a l c a ] siz[u]+siz[v]-siz[lca]-siz[falca] siz[u]+siz[v]siz[lca]siz[falca]
其中falca是lca的父节点,如果lca没有父节点,应该初始化为0。

然后就是加边操作,因为题目已经保证这是一个森林,因此不会成环,我们可以放心用并查集找到根节点,但不能路径压缩,因为我们需要知道整棵树的结构,这时候就可以用到启发式合并了。我们将siz较小的树并到siz较大的树上可以保证时间复杂度在NlogN。

Code

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
const int INF = 0x3f3f3f3f;
const int N = 2e5 + 10;
const int M = 1e5 + 10;
const int MOD = 998244353;
int a[N], b[N], n, m, k, tot, cnt;
int dep[N], f[N][20], vis[N], siz[N], fa[N];
int rt[N];
vector<int> g[N];
int find(int x) {return x == fa[x] ? x : fa[x] = find(fa[x]);}
struct node {
    int ls, rs;
    int sum;
}hjt[N * 50];
int Hash(int x) {
    return lower_bound(b + 1, b + cnt + 1, x) - b;
}
void modify(int &now, int pre, int l, int r, int pos) {
    now = ++tot;
    hjt[now] = hjt[pre];
    hjt[now].sum = hjt[pre].sum + 1;
    if (l == r) return;
    int mid = (l + r) >> 1;
    if (pos <= mid) modify(hjt[now].ls, hjt[pre].ls, l, mid, pos);
    else modify(hjt[now].rs, hjt[pre].rs, mid + 1, r, pos);
}
int query(int u, int v, int lca, int falca, int l, int r, int K) {
    if (l == r) return b[l];
    int num = hjt[hjt[u].ls].sum + hjt[hjt[v].ls].sum - hjt[hjt[lca].ls].sum - hjt[hjt[falca].ls].sum;
    int mid = (l + r) >> 1;
    if (K <= num) return query(hjt[u].ls, hjt[v].ls, hjt[lca].ls, hjt[falca].ls, l, mid, K);
    else return query(hjt[u].rs, hjt[v].rs, hjt[lca].rs, hjt[falca].rs, mid+1, r, K-num);
}
void dfs(int x, int far, int Rt) {
    dep[x] = dep[far] + 1; f[x][0] = far;
    for (int i = 1; i <= 17; i++) f[x][i] = f[f[x][i-1]][i-1];
    siz[Rt]++; fa[x] = far;
    vis[x] = 1;
    modify(rt[x], rt[far], 1, cnt, Hash(a[x]));
//    cout << x << " " << Hash(a[x]) << endl;
    for (auto v : g[x]) {
        if (v == far) continue;
        dfs(v, x, Rt);
    }
}
int lca(int x, int y) {
    if (dep[x] > dep[y]) swap(x, y);
    for (int i = 17; i >= 0; i--) if (dep[y] - dep[x] >= (1 << i)) y = f[y][i];

    if (x == y) return x;
    for (int i = 17; i >= 0; i--) {
        if (f[y][i] != f[x][i])
            y = f[y][i], x = f[x][i];
    }
    return f[x][0];
}
void solve () {
    int T; cin >> T;
    T = 1; while (T--) {
        cin >> n >> m >> k;
        for (int i = 1; i <= n; i++) {
            cin >> a[i];
            b[i] = a[i];
            fa[i] = i;
        }
        for (int i = 1; i <= m; i++) {
            int u, v; cin >> u >> v;
            g[u].push_back(v);
            g[v].push_back(u);
        }
        sort(b + 1, b + n + 1);
        cnt = unique(b + 1, b + n + 1) - b - 1;
        for (int i = 1; i <= n; i++) {
            if (!vis[i]) {
                dfs(i, 0, i);
                fa[i] = i;
            }
        }
        int lastans = 0;
        for (int i = 1; i <= k; i++) {
            char op;
            int x, y, rk;
            cin >> op;
            if (op == 'Q') {
                cin >> x >> y >> rk;
                x ^= lastans, y ^= lastans, rk ^= lastans;
                int _lca = lca(x, y);
                int fa_lca = f[_lca][0];
                lastans = query(rt[x], rt[y], rt[_lca], rt[fa_lca], 1, cnt, rk);
                cout << lastans << endl;
            } else {
                cin >> x >> y;
                x ^= lastans, y ^= lastans;
                g[x].push_back(y);
                g[y].push_back(x);
                int fx = find(x);
                int fy = find(y);
                if (siz[fx] > siz[fy]) {
                    swap(fx, fy);
                    swap(x, y);
                }
                dfs(x, y, fy);

            }
        }
    }
}
signed main() {
    ios_base::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
#ifdef ACM_LOCAL
    freopen("input", "r", stdin);
    freopen("output", "w", stdout);
    signed test_index_for_debug = 1;
    char acm_local_for_debug = 0;
    do {
        if (acm_local_for_debug == '$') exit(0);
        if (test_index_for_debug > 20)
            throw runtime_error("Check the stdin!!!");
        auto start_clock_for_debug = clock();
        solve();
        auto end_clock_for_debug = clock();
        cout << "Test " << test_index_for_debug << " successful" << endl;
        cerr << "Test " << test_index_for_debug++ << " Run Time: "
             << double(end_clock_for_debug - start_clock_for_debug) / CLOCKS_PER_SEC << "s" << endl;
        cout << "--------------------------------------------------" << endl;
    } while (cin >> acm_local_for_debug && cin.putback(acm_local_for_debug));
#else
    solve();
#endif
    return 0;
}




以上是关于P3302 [SDOI2013]森林 主席树+LCA+启发式合并的主要内容,如果未能解决你的问题,请参考以下文章

P3302 [SDOI2013]森林

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

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

「Luogu P3302」[SDOI2013]森林

P3302 SDOI2013森林

[SDOI2013]森林(树上主席树)