标签:roo har std 查询 text namespace head 路径 names
给定 \(n\) 个点,要求支持操作:
考虑这题可以离线,先把树的结构建出来。
考虑动态维护每个节点的子树大小 \(size_i\) 与当前联通快的根节点 \(root_i\)。
对于 \(root\) 的维护,用并查集即可,做到 \(O(N\log_2N)\)
对于 \(size\) 的维护,可以暴力树链剖分,也可以差分 \(+\) 树状数组(详情看 Loj #146. DFS 序 3,树上差分 1),我用了后者 \(O(Nlog_2N)\)。
\(O(N \log_2N)\)
#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;
}
标签:roo har std 查询 text namespace head 路径 names
原文地址:https://www.cnblogs.com/dmoransky/p/12546151.html