[结论] CF1067E Random Forest Rank

\(\texttt{link}\)

结论: 一个森林的邻接矩阵的秩等于该森林的最大匹配数的两倍。

\(\color{red}{\text{证明:}}\)

对于矩阵 \(A\) 的秩 \(r(A)\) ,其满足:若矩阵 \(A\) 存在不为零的 \(k\) 阶子式,则有 \(r(A) \ge k\)

也就是说,找到最大的 \(k\) 满足矩阵 \(A\) 存在不为零的 \(k\) 阶子式,就能得到 \(r(A) = k\)

对于矩阵 \(A\) 的一个非零子式,由行列标号的并组成的主子式也是非零子式(邻接矩阵是对称的),因此只需要考虑 \(k\) 阶主子式。

矩阵 \(A\) 的一个 \(k\) 阶主子式相当于森林的一个大小为 \(k\) 的导出子图的邻接矩阵的行列式,考虑这个行列式的展开:

\[det(A) = \sum\limits_{p}\operatorname{sgn}(p)\prod\limits_{i=1}^n A_{i,p_i} \]

森林的导出子图也是个森林,不存在自环,则存在 \(i=p_i\) 的排列贡献为 \(0\);并且不存在大小 \(\ge 3\) 的环,所以有贡献的排列必然是由若干个大小为二的置换环构成,这相当于是一个完美匹配

森林的完美匹配方案必然 \(\le 1\),因此非零的主子式贡献为完美匹配数 \(\times 2\)

于是一个森林的邻接矩阵的秩就等于该森林的最大匹配数的两倍,证毕。

有了这个结论直接树形 \(dp\) ,记 \(f(u,0/1)\) 表示点 \(u\) 是否与儿子匹配,子树 \(u\) 的所有情况的最大匹配数的和, \(g(u,0/1)\) 则表示对应的方案数,转移时分类讨论即可。

当然也有更简洁的 \(dp\),记 \(f(u)\) 表示点 \(u\) 与其中一个儿子匹配的概率,转移也分两种:

  • 不存在边:\(\dfrac 1 2\) 的概率;

  • 存在边但是儿子已经在匹配中: \(\dfrac {f_{son(u)}} 2\) 的概率;

则实际的 \(f(u)\) 求补集即可。

代码是第二种 \(dp:\)

#include <bits/stdc++.h>
#define pb push_back
using namespace std;
const int cmd = 998244353;
int add(int a, int b) {a += b; return a < cmd ? a : a - cmd;}
int mul(int a, int b) {return 1ll * a * b % cmd;}
int fpow(int a, int b) {
    int res = 1;
    for (; b; b >>= 1, a = mul(a, a))
        if (b & 1) res = mul(res, a);
    return res;
}
const int maxn = 1e6 + 5;
int n, f[maxn], inv2, ans;
vector<int> G[maxn];
void dfs(int u, int fa) {
    f[u] = 1;
    for (int i = 0; i < G[u].size(); i++) {
        int v = G[u][i];
        if (v == fa) continue;
        dfs(v, u);
        f[u] = mul(f[u], mul(1 + f[v], inv2));
    }f[u] = add(1, cmd - f[u]);
    ans = add(ans, mul(2, f[u]));
}
int main() {
    scanf("%d", &n);
    inv2 = fpow(2, cmd - 2);
    for (int i = 1; i < n; i++) {
        int u, v; scanf("%d%d", &u, &v);
        G[u].pb(v); G[v].pb(u);
    }dfs(1, 0);
    for (int i = 1; i < n; i++) ans = ans * 2 % cmd;
    printf("%d", ans);
    return 0;
}
posted @ 2022-02-12 12:02  klii  阅读(108)  评论(0)    收藏  举报