[The 2023 ICPC AROC I] G Spanning Tree 题解
[The 2023 ICPC AROC I] G Spanning Tree 题解
思路
求生成指定生成树的概率,其实也就是 \(1/\)生成树个数,前提是该生成树合法。
而生成树个数可以表示为,每次合并集合时,两侧集合大小的乘积,即 \(\prod_{(l,r)\in\mathit{op}}\mathit{cnt}_l\times\mathit{cnt}_r\)。
而如何判断合法呢?并查集维护连通块在树上的最高点,那么如果生成树合法,每次加的边一定在树上是某一侧连通块最高点到其父亲的边。
不会证明(逃
然后就是求 \(1/x\) 对 \(998244353\) 取模了,见我的文章 乘法逆元,其实也就是 \(x^{998244351}\bmod998244343\) 而已,快速幂求解即可。
时间复杂度:\(\mathcal{O}(n\log n)\);空间复杂度:\(\mathcal{O}(n)\)。
代码
#include <bits/stdc++.h>
using std::vector;
using std::tie;
using std::swap;
using pii = std::pair<int, int>;
using vpi = vector<pii>;
using ll = long long;
#define rep(i, x) for (int i = 0; i < (x); ++i)
#define gor(i, x, y) for (int i = x; i < (y); ++i)
#define rr read()
inline int read() {
int num = 0, flag = 1, ch = getchar();
for (; !isdigit(ch); ch = getchar()) if (ch == '-') flag = -1;
for (; isdigit(ch); ch = getchar()) num = (num << 3) + (num << 1) + ch - '0';
return num * flag;
}
const int N = 1e6 + 10;
const int MOD = 998244353;
vector<int> g[N];
void add(int u, int v) {
g[u].push_back(v), g[v].push_back(u);
}
int p[N], d[N];
void dfs(int u, int fa) {
for (int v : g[u]) if (v != fa)
d[v] = d[u] + 1, p[v] = u, dfs(v, u);
}
int f[N], c[N];
int find(int x) {
return x == f[x] ? x : f[x] = find(f[x]);
} void merge(int a, int b) {
if (d[a] < d[b]) swap(a, b);
f[a] = b, c[b] += c[a];
}
ll qpow(ll a, ll b, ll m) {
ll res = 1;
for (; b; b >>= 1, a = a * a % m) if (b & 1) res = res * a % m;
return res;
}
signed main() {
int n = rr; vpi op(n - 1); rep(i, n - 1) op[i] = {rr, rr};
rep(i, n - 1) add(rr, rr); gor(i, 1, n + 1) f[i] = i, c[i] = 1;
d[1] = 1; dfs(1, -1); ll res = 1; for (pii k : op) {
int a, b; tie(a, b) = k; a = find(a), b = find(b);
if (find(p[a]) != b && find(p[b]) != a) printf("0\n"), exit(0);
(res *= (1ll * c[a] * c[b]) % MOD) %= MOD; merge(a, b);
} printf("%lld\n", qpow(res, MOD - 2, MOD));
return 0;
}
本文来自博客园,作者:RainPPR,转载请注明原文链接:https://www.cnblogs.com/RainPPR/p/solution-icpc2023aroc-1g.html
如有侵权请联系我(或 2125773894@qq.com)删除。