HDU 6836 Expectation (生成树计数+按位计算期望)

题意:给出一张无向图,求随机选择一个生成树的权值的期望。权值为mst的边权按位与后的结果。

题解:生成树计数+按位计算期望
总的生成树数量直接根据矩阵树定理求一下。

由于权值是按位与之后的结果,也就是说只有这颗mst所有权值的第 i 位都为1时才会有贡献,那我们单独考虑每一位的贡献。

遍历所有位,对于该位上为1的边连起来,然后跑生成树计数,该位的贡献就是2i * 数量。

#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define ll long long
using namespace std;
const int MOD = 998244353;
int t, n, m, u, v, w;
struct Matrix {
    int mat[110][110];
    void init() {
        memset(mat, 0, sizeof(mat));
    }
    int det(int n) {
        ll res = 1;
        for (int i = 1; i <= n; i++) {
            if (!mat[i][i]) { //若果对角线元素为0,把此行都一都移到下一行去
                bool flag = false;
                for (int j = i + 1; j <= n; j++) { //从i+1行开始找i列中的第一个不为0的元素,与现在的行交换
                    if (mat[j][i]) {//找到了该列不为0的元素,
                        flag = 1; //标记,交换
                        for (int k = i; k <= n; k++) swap(mat[i][k], mat[j][k]);
                        res = -res;// 换行系数变为负数
                        break; //退出.
                    }
                }
                if (!flag) return 0; //这一行全部为0,行列式值为0
            }
            for (int j = i + 1; j <= n; j++) {
                while (mat[j][i]) { //从下面的行找一个不为0的元素与第i行进行消元
                    ll t = mat[i][i] / mat[j][i];
                    for (int k = i; k <= n; k++) {
                        mat[i][k] = (mat[i][k] - t * mat[j][k]) % MOD;
                        swap(mat[i][k], mat[j][k]);//消元后,把0的行换到下面来。
                    }
                    res = -res;
                }
            }
            res *= mat[i][i];//对角线元素相乘
            res %= MOD;
        }
        return (res + MOD) % MOD;
    }
}ma;
struct node {
    int u, v, w;
}edge[11111];
ll fastpow(ll base, ll n, ll mod) {
    ll ans = 1;
    while (n) {
        if (n & 1) ans *= base % mod, ans %= mod;
        base *= base, base %= mod;
        n >>= 1;
    }
    return ans % mod;
}
int main() {
	scanf("%d", &t);
	while (t--) {
        ma.init();
		scanf("%d%d", &n, &m);
		for (int i = 1; i <= m; i++) {
			scanf("%d%d%d", &u, &v, &w);
            edge[i] = { u, v, w };
            ma.mat[u][u]++;
            ma.mat[v][v]++;
            ma.mat[u][v]--;
            ma.mat[v][u]--;
		}
        ll sum = ma.det(n - 1), ans = 0;
        for (int i = 0; i <= 30; i++) {
            ma.init();
            for (int j = 1; j <= m; j++) {
                if (edge[j].w & (1 << i)) {
                    u = edge[j].u;
                    v = edge[j].v;
                    ma.mat[u][u]++;
                    ma.mat[v][v]++;
                    ma.mat[u][v]--;
                    ma.mat[v][u]--;
                }
            }
            int temp = ma.det(n - 1);
            ans = (ans + 1ll * (1 << i) * temp % MOD) % MOD;
        }
        printf("%lld\n", ans * fastpow(sum, MOD - 2, MOD) % MOD);
	}
	return 0;
}

你可能感兴趣的:(#,生成树,概率&期望,生成树计数,期望)