BZOJ-3123: [Sdoi2013]森林(主席樹 + LCA + 啓發式合併)

題目鏈接:https://www.lydsy.com/JudgeOnline/problem.php?id=3123

題目大意:給出一個有n個節點的森林,接下來有m次操作,每次操作有以下兩種:

1、Q x y k : 詢問節點 x 到節點 y 這條鏈上的點的第 k 大的權值是多少。(x和y保證在一個聯通塊內)

2、L x y : 在節點 x 和節點 y 之間連一條邊。(x和y保證不在一個聯通塊內)

同時這些操作都是強制在線的。

題目思路:如果只有第一種操作,由於是強制在線的,我們優先考慮藉助主席樹和LCA來做,做一遍dfs,每個節點以其父節點爲前置節點更新主席樹就行,詢問的時候就是用主席樹查詢 sum[x] + sum[y] - sum[LCA(x,y)] - sum[fa[LCA(x,y)]]

現在來考慮第二種情況,由於x 和 y不屬於同一個聯通塊,那麼這兩個點連一條邊之後,其所在兩個樹會合併成一個新的樹,對於這一個新的樹我們如果重新選擇根節點再重新dfs建主席樹的話,這個時間複雜度是不合理的。

我們可以考慮用啓發式合併的思想對這兩個樹合併,先考慮兩個樹的節點個數,將節點個數較少的點(x)接到節點個數較多的那個樹(y)下方,更新一下節點個數較少的點的根節點爲 x 節點,x節點的父節點自然就爲y節點了,再對x節點做一遍dfs,更新一下LCA和主席樹的信息。由於一直是小的往大的去合併,所以總的更新次數就會比你隨便選擇根節點的更新次數要小很多,所以時間複雜度也是合理的。

具體實現看代碼:

#include <bits/stdc++.h>
#define fi first
#define se second
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define pb push_back
#define MP make_pair
#define lowbit(x) x&-x
#define clr(a) memset(a,0,sizeof(a))
#define _INF(a) memset(a,0x3f,sizeof(a))
#define FIN freopen("in.txt","r",stdin)
#define IOS ios::sync_with_stdio(false)
#define fuck(x) cout<<"["<<#x<<" "<<(x)<<"]"<<endl
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int>pii;
const int MX = 1e5 + 5;
const int N = 4e7;
const int inf = 0x3f3f3f3f;

int n, m, q;
int val[MX];
int sum[N], ls[N], rs[N];
int root[MX], all;
struct edge {int v, nxt;} E[MX << 1];
int head[MX], tot;
int dep[MX], ST[MX][20], sz[MX];
int vis[MX], belong[MX], tree_rt[MX];

void init(int _n) {
    for (int i = 1; i <= _n; i++) {
        vis[i] = belong[i] = root[i] = dep[i] = 0;
        head[i] = -1;
    }
    tot = all = 0;
}
void add_edge(int u, int v) {
    E[tot].v = v; E[tot].nxt = head[u];
    head[u] = tot++;
}
void update(int l, int r, int &rt, int pre, int pos, int v) {
    rt = ++all;
    ls[rt] = ls[pre]; rs[rt] = rs[pre]; sum[rt] = sum[pre] + v;
    if (l == r) return;
    int mid = (l + r) >> 1;
    if (pos <= mid) update(l, mid, ls[rt], ls[pre], pos, v);
    else update(mid + 1, r, rs[rt], rs[pre], pos, v);
}
int query(int l, int r, int u, int v, int lca, int flca, int k) {
    if (l == r) return l;
    int res = sum[ls[u]] + sum[ls[v]] - sum[ls[lca]] - sum[ls[flca]];
    int mid = (l + r) >> 1;
    if (res >= k) return query(l, mid, ls[u], ls[v], ls[lca], ls[flca], k);
    else return query(mid + 1, r, rs[u], rs[v], rs[lca], rs[flca], k - res);
}
void dfs(int u, int fa, int id) {
    vis[u] = 1; belong[u] = id; sz[u] = 1;
    for (int i = 1; i < 20; i++)
        ST[u][i] = ST[ST[u][i - 1]][i - 1];
    update(1, inf, root[u], root[fa], val[u], 1);
    for (int i = head[u]; ~i; i = E[i].nxt) {
        int v = E[i].v;
        if (v == fa) continue;
        dep[v] = dep[u] + 1;
        ST[v][0] = u;
        dfs(v, u, id);
        sz[v] += sz[u];
    }
}
int LCA(int u, int v) {
    while (dep[u] != dep[v]) {
        if (dep[u] < dep[v]) swap(u, v);
        int d = dep[u] - dep[v];
        for (int i = 0; i < 20; i++)
            if (d >> i & 1) u = ST[u][i];
    }
    if (u == v) return u;
    for (int i = 19; i >= 0; i--) {
        if (ST[u][i] != ST[v][i]) {
            u = ST[u][i];
            v = ST[v][i];
        }
    }
    return ST[u][0];
}
void Merage(int u, int v) {
    int rt1 = tree_rt[belong[u]], rt2 = tree_rt[belong[v]];
    if (sz[rt1] > sz[rt2]) swap(rt1, rt2), swap(u, v);
    add_edge(u, v); add_edge(v, u);
    ST[u][0] = v; sz[rt2] += sz[rt1]; dep[u] = dep[v] + 1;
    dfs(u, v, belong[v]);
}

int main() {
    // FIN;
    int T; scanf("%d", &T);
    scanf("%d%d%d", &n, &m, &q);
    init(n);
    for (int i = 1; i <= n; i++) scanf("%d", &val[i]);
    for (int i = 1; i <= m; i++) {
        int u, v;
        scanf("%d%d", &u, &v);
        add_edge(u, v); add_edge(v, u);
    }
    int cnt = 0;
    for (int i = 1; i <= n; i++) {
        if (!vis[i]) {
            dfs(i, 0, ++cnt);
            tree_rt[cnt] = i;
        }
    }
    char op[2];
    int u, v, k;
    int ans = 0;
    while (q--) {
        scanf("%s%d%d", op, &u, &v);
        u ^= ans; v ^= ans;
        if (op[0] == 'Q') {
            scanf("%d", &k);
            k ^= ans;
            int lca = LCA(u, v);
            ans = query(1, inf, root[u], root[v], root[lca], root[ST[lca][0]], k);
            printf("%d\n", ans);
        } else
            Merage(u, v);
    }
    return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章