「解题报告」CF1067E Random Forest Rank
感觉非常强大。
求秩不好考虑,容易想到求行列式。如果行列式不等于 \(0\) 说明满秩。
先考虑树邻接矩阵的行列式:行列式的定义式即枚举一个排列。排列可以划分成若干个置换环,这相当于要求图上的若干个环。
而考虑到树中没有大小大于等于 \(3\) 的环,只有 \(2\) 的环(一条边),那么树的邻接矩阵的行列式就相当于是求一个完美匹配。而树的完美匹配最多只有一个,所以行列式不等于 \(0\) 当且仅当树存在完美匹配。
考虑秩如何计算:秩等于矩阵中不为零的子式的最大阶数称为矩阵A的秩。子式是指选出若干行列,把交叉处的数留下来形成新的矩阵。
假如我们选出的行列不相等,那么就会出现单向边,而这意味着不可能存在完美匹配。而若我们选出的行列相等,相当于选出一个点集,使得这个点集合的导出子图存在完美匹配。这实际上意味着树的最大匹配。
那么我们就把问题转换成了:对于一颗树,求最大匹配的期望值。
考虑对树求最大匹配的过程:贪心选取,能匹配则匹配。注意到虽然最大匹配不止一种,但是由这种贪心策略得出的最大匹配是唯一的。
那么我们可以完全按照求最大匹配的过程来求概率。根据期望的线性性,最大匹配的期望就是每条边在最大匹配中的概率的和。简单树形 DP 即可。
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 500005, P = 998244353, I = (P + 1) / 2;
int n;
int qpow(int a, int b) {
int ans = 1;
while (b) {
if (b & 1) ans = 1ll * ans * a % P;
a = 1ll * a * a % P;
b >>= 1;
}
return ans;
}
vector<int> e[MAXN];
int f[MAXN][2];
void dfs(int u, int pre) {
int prob = 1;
f[u][0] = 1;
for (int v : e[u]) if (v != pre) {
dfs(v, u);
f[u][1] = (f[u][1] + 1ll * prob * I % P * f[v][0]) % P;
f[u][0] = 1ll * f[u][0] * I % P * (f[v][1] + 1) % P;
prob = 1ll * prob * I % P * (f[v][1] + 1) % P;
}
}
int main() {
scanf("%d", &n);
for (int i = 1; i < n; i++) {
int u, v; scanf("%d%d", &u, &v);
e[u].push_back(v);
e[v].push_back(u);
}
dfs(1, 0);
int ans = 0;
for (int i = 1; i <= n; i++) {
ans = (ans + f[i][1]) % P;
}
ans = 1ll * ans * qpow(2, n) % P;
printf("%d\n", ans);
return 0;
}