HDU 6836 Expectation(期望,矩阵树定理)

1010 Expectation(期望,矩阵树定理)

传送门

题目大意

  给一个无向连通图,任意从图中取出一个生成树(该图的所有生成树等概率被取到),求该生成树每条边权进行位运算AND后的期望值。

解题思路

  位运算题目通用的思路,按位计算,累加贡献。我们可以只看每条边权的第i位,这样就可以把这个图的所有边权变成0和1(1表示原来边权在第i位上的值是1,0同理)。根据期望的线性性质: E ( X + Y ) = E ( X ) + E ( Y ) E(X + Y) = E(X) + E(Y) E(X+Y)=E(X)+E(Y)所求的期望值就等于每一位期望值乘上这一位对应的权值求和,即(设考虑第 i i i位时的期望值位 E i E_i Ei E = ∑ E i ⋅ 2 i E = \sum E_i \cdot2^i E=Ei2i

算法实现

  求期望值 E i E_i Ei要先求概率 P i P_i Pi,也就是在这张由0,1边组成的图中,任意选出一个生成树,边权AND值为1的概率。显然根据AND的性质必须要求所选的生成树每条边都为1。这样就可以用所有1边组成的图的生成树的个数,除以原图的总的生成树个数来表示概率。期望就是概率乘上收益,及刚才算出的概率乘上1,即 E i = P i ⋅ 1 + ( 1 − P i ) ⋅ 0 = P i E_i = P_i \cdot 1 + (1 - P_i)\cdot 0 = P_i Ei=Pi1+(1Pi)0=Pi
  至于如何求无向图生成树的个数,可以用矩阵树定理来求。具体细节参考代码:

#include 
using namespace std;
#define ll long long
const int MAXN = 2e2 + 5;
const int Mod = 998244353;

ll mpow(ll x, ll b) {
	ll ret = 1ll;
	for (; b; b >>= 1, x = x * x % Mod)
 		if (b & 1) ret = ret * x % Mod;
 	return ret;
}
// 求基尔霍夫矩阵A的行列式 
ll determinant(int n, ll a[][MAXN]) {
	char sign = 0;
	ll ans = 1ll;
	for (int i = 1; i <= n; i++) {
		for (int j = i + 1; j <= n; j++) {
			int x = i, y = j;
			// 利用类似gcd的迭代方式来消元 
			while (a[y][i]) {
				ll t = a[x][i] / a[y][i];
				for (int k = i; k <= n; k++) 
					a[x][k] = (a[x][k] - a[y][k] * t) % Mod;
				swap(x, y); 
			}
			// 最后a[i][i]等于0,a[x][i]不等于0,要把i行和x行交换。 
			if (x != i) {
				for (int k = 1; k <= n; k++) 
					swap(a[i][k], a[x][k]);
				sign ^= 1; 
			}
		}
		ans = ans * a[i][i] % Mod;
	}
	if (sign) ans = -ans;
	if (ans < 0) ans += Mod;
	return ans;
}
ll G[MAXN][MAXN][30], A[MAXN][MAXN];
int n, m;

int main() {
	std::ios::sync_with_stdio(false);
	cin.tie(NULL), cout.tie(NULL);
	int T; cin >> T;
	while (T--) {
		cin >> n >> m;
		memset(G, 0, sizeof(G));
		memset(A, 0, sizeof(A));
		for (int u, v, w, i = 1; i <= m; i++) {
			cin >> u >> v >> w;
			for (int i = 0; (1l << i) <= w; i++) 
				if (w & (1l << i)) G[u][v][i] = ++G[v][u][i];
			A[u][v] = --A[v][u]; 
			++A[u][u], ++A[v][v];
		}
		ll inv = mpow(determinant(n - 1, A), Mod - 2), ans = 0;
		// 按位计算,累加贡献 
		for (int k = 0; (1l << k) <= 1e9; k++) {
			memset(A, 0, sizeof(A));
			for (int i = 1; i <= n; i++) 
				for (int j = 1; j <= n; j++) 
					if (G[i][j][k]) A[i][j] = -G[i][j][k], A[i][i] += G[i][j][k];
			ans = (ans + (1ll << k) * determinant(n - 1, A) % Mod * inv % Mod) % Mod;		
 		}
 		cout << ans << endl;
	}
	return 0; 
}

你可能感兴趣的:(HDU 6836 Expectation(期望,矩阵树定理))