BJOI2014 大融合

落谷Loj

Description

给定 (n) 个点,要求支持操作:

  1. 连接 ((x, y)) 这条边
  2. 求当前状态下经过 ((x, y)) 这条边的路径数量

Solution

考虑这题可以离线,先把树的结构建出来。

考虑动态维护每个节点的子树大小 (size_i) 与当前联通快的根节点 (root_i)

  • 对于连接操作,设在我们建的结构中 (x)(y) 的父亲(否则 ( ext{swap})),链接这条边,相当于将 (x Rightarrow root_x) 这条链上的 (size)(+ size_y),即一个链加操作。然后将 (y) 联通块的 (root) 都赋值为 (root_x)
  • 对于查询操作,同样设在我们建的结构中 (x)(y) 的父亲(否则 ( ext{swap})),答案即为 (size_{y} * (size_{root_x} - size_{y}))

对于 (root) 的维护,用并查集即可,做到 (O(Nlog_2N))

对于 (size) 的维护,可以暴力树链剖分,也可以差分 (+) 树状数组(详情看 Loj #146. DFS 序 3,树上差分 1),我用了后者 (O(Nlog_2N))

时间复杂度

(O(N log_2N))

Code

#include <iostream>
#include <cstdio>
const int N = 100005;

using namespace std;

typedef long long LL;

int n, m, rt[N], sz[N], dfn[N], dfncnt, fa[N];
int opt[N], A[N], B[N], c[N];

int head[N], numE = 0;

struct E {
    int next, v;
} e[N << 1];

inline void addEdge(int u, int v) {
    e[++numE] = (E){ head[u], v };
    head[u] = numE;
}

int find(int x) { return rt[x] == x ? x : rt[x] = find(rt[x]); }

void inline add(int x, int k) {
    if (x == 0)
        return;
    for (; x <= n; x += x & -x) c[x] += k;
}

int inline ask(int x) {
    int res = 0;
    for (; x; x -= x & -x) res += c[x];
    return res;
}

void dfs(int u) {
    dfn[u] = ++dfncnt, sz[u] = 1;
    for (int i = head[u]; i; i = e[i].next) {
        int v = e[i].v;
        if (v == fa[u])
            continue;
        fa[v] = u;
        dfs(v);
        sz[u] += sz[v];
    }
}

int inline query(int x) { return ask(dfn[x] + sz[x] - 1) - ask(dfn[x] - 1) + 1; }

int main() {
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++) rt[i] = i;
    for (int i = 1; i <= m; i++) {
        char c[2];
        scanf("%s%d%d", c, A + i, B + i);
        opt[i] = c[0] == 'A' ? 0 : 1;
        if (!opt[i])
            addEdge(A[i], B[i]), addEdge(B[i], A[i]);
    }
    for (int i = 1; i <= n; i++)
        if (!dfn[i])
            dfs(i);
    for (int i = 1; i <= m; i++) {
        int x = A[i], y = B[i];
        if (y == fa[x])
            swap(x, y);
        if (opt[i] == 0) {
            // 连接
            rt[find(y)] = find(x);
            x = find(x);
            int v = query(y);
            add(dfn[fa[x]], -v), add(dfn[fa[y]], v);
        } else {
            // 查询
            x = find(x);
            printf("%lld
", (LL)query(y) * (query(x) - query(y)));
        }
    }
    return 0;
}
原文地址:https://www.cnblogs.com/dmoransky/p/12546151.html