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;
}

猜你喜欢

转载自blog.csdn.net/Lee_w_j__/article/details/83958074
今日推荐