「解题报告」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;
}
posted @ 2023-03-11 16:27  APJifengc  阅读(30)  评论(0编辑  收藏  举报