BJOI2014 大融合

落谷Loj

Description

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

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

Solution

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

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

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

对于 \(root\) 的维护,用并查集即可,做到 \(O(N\log_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\n", (LL)query(y) * (query(x) - query(y)));
        }
    }
    return 0;
}
posted @ 2020-03-22 15:18  DMoRanSky  阅读(130)  评论(0编辑  收藏  举报