2020 HDU 多校第六场 1010 Expectation 期望 矩阵树定理
Posted lasomisolaso~
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2020 HDU 多校第六场 1010 Expectation 期望 矩阵树定理相关的知识,希望对你有一定的参考价值。
题意:
首先定义了一棵生成树的重量:这棵树所有边的按位与(AND)
给出一个无向连通图,随机挑选一个生成树,问这个生成树的重量的期望是多少。
题解:
做法:
数学期望的性质: E ( X + Y ) = E ( X ) + E ( Y ) E(X + Y) = E(X) + E(Y) E(X+Y)=E(X)+E(Y)
本题中给出的生成树的重量的定义:所有边的边权的按位与,我们可以拆开,按位来考虑。
利用数学期望的性质,可以得到下面的式子:
E ( a n s ) = E ( x 31 ∗ 2 31 + x 30 ∗ 2 30 + . . . + x 0 ∗ 2 0 ) = E ( x 31 ∗ 2 31 ) + E ( x 30 ∗ 2 30 ) + . . . + E ( x 0 ∗ 2 0 ) E(ans) = E(x_31*2^31 + x_30*2^30 + ... + x_0*2^0) = E(x_31*2^31) + E(x_30*2^30) + ... + E(x_0*2^0) E(ans)=E(x31∗231+x30∗230+...+x0∗20)=E(x31∗231)+E(x30∗230)+...+E(x0∗20)
下面我们只要考虑每一位就可以了。
第 i i i位要怎样才能是1呢, 要所有边的边权的第 i i i位都是1。
我只要把所有的第 i i i位是 1 1 1的边找出来,然后计算一下这些边能够构成多少个生成树。那这个生成树的数量就是重量第 i i i位为 1 1 1的所有生成树。设这个数量为 n u m num num,这张图总生成树数量为 s u m sum sum。
那期望就能算出来, E ( x i ∗ 2 i ) = 2 i ∗ n u m s u m E(x_i*2^i) = 2^i * num \\over sum E(xi∗2i)=2i∗sumnum。
依次计算32位,最后相加就行了。
然后生成树个数的计算,使用矩阵树定理就能计算出来。
矩阵树定理:
矩阵树定理参考博客地址
百度百科:
求出基尔霍夫矩阵之后,只要求它的一个代数余子式即可。下面的模板是求 a n n a_nn ann的代数余子式,即 ( − 1 ) n + n ∗ M n n (-1)^n + n*M_nn (−1)n+n∗Mnn,因为 n + n n + n n+n一定是偶数,所以也就是求去掉第 n n n行,第 n n n列之后的 n − 1 n-1 n−1阶方阵的行列式。
行列式是转换成上三角之后,对角线元素乘积直接得到。
另:高斯消元部分模板是采用辗转相除的方式消去下面的行,因为答案要取模。与求GCD的方式类似,不断相除,总有一个会变成 0 0 0。
高斯消元过程还涉及到交换两行的操作,根据行列式的性质,交换两行,行列式变为原来的负数。
代码:
/*
* @file 1010.cpp
* @path D:\\code\\ACM\\HDU\\no.6\\1010.cpp
* @author Xiuchen
* @date 2020-08-07 11:47:57
*/
#pragma GCC optimize(1)
#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<string>
#include<vector>
#include<stack>
#include<queue>
#include<map>
#include<cmath>
#include<math.h>
#include<iostream>
#include<algorithm>
//#define DEBUG
#define dbg(x) cout << #x << " = "<< (x) << endl
#define dbg2(x1,x2) cout << #x1 << " = " << x1 << " " << #x2 << " = " << x2 << endl
#define dbg3(x1,x2,x3) cout<< #x1 << " = " << x1 << " " << #x2 << " = " << x2 << " " << #x3 << " = " << x3 <<endl
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3fLL;
const int maxn = 11000;
const ll mod = 998244353;
int gcd(int a, int b)
return b ? gcd(b, a % b) : a;
int t;
int n, m;
struct node
int u, v;
ll w;
edge[maxn];
int k[110][110];
ll sum = 0;
ll qpow(ll x, ll y)
x %= mod;
ll ans = 1, base = x;
while(y)
if(y & 1) ans = ans * base % mod;
base = base * base % mod;
y >>= 1;
return ans;
ll gauss(int n)
ll res = 1;
//转换成上三角
for(int i = 1; i <= n - 1; i++)
for(int j = i + 1; j <= n - 1; j++)
while(k[j][i]) // 辗转相除,之后交换行。
ll d = k[i][i] / k[j][i];
for(int o = 1; o <= n - 1; o++)
//注意取模
k[i][o] = (k[i][o] - d * k[j][o] + mod) % mod;
swap(k[i], k[j]);
res = -res;
//注意取模
res = res * k[i][i] % mod;
return (res + mod) % mod);
int main()
#ifdef DEBUG
freopen("input.txt", "r", stdin);
// freopen("output.txt", "w", stdout);
#endif
scanf("%d", &t);
while(t--)
scanf("%d%d", &n, &m);
memset(k, 0, sizeof(k));
for(int i = 1; i <= m; i++) scanf("%d%d%lld", &edge[i].u, &edge[i].v, &edge[i].w);
for(int i = 1; i <= m; i++)
int x = edge[i].u;
int y = edge[i].v;
// k是基尔霍夫矩阵。
k[x][x]++;
k[y][y]++;
k[x][y]--;
k[y][x]--;
sum = gauss(n);
ll ans = 0;
// 遍历每一位
for(int i = 0; i < 32; i++)
memset(k, 0, sizeof(k));
for(int j = 1; j <= m; j++)
if(edge[j].w & (1LL << i))
int x = edge[j].u;
int y = edge[j].v;
k[x][x]++;
k[y][y]++;
k[x][y]--;
k[y][x]--;
ll tmp = gauss(n);
ans = (ans + tmp * (1ll << i) % mod * qpow(sum, mod - 2) % mod) % mod;
printf("%lld\\n", ans);
return 0;
以上是关于2020 HDU 多校第六场 1010 Expectation 期望 矩阵树定理的主要内容,如果未能解决你的问题,请参考以下文章
2020hdu多校第六场1006A Very Easy Graph Problem
2019HDU多校第六场1009 Three Investigators——杨表
2019杭电多校第六场hdu6638 Snowy Smile(线段树+枚举)
杭电2018多校第六场(2018 Multi-University Training Contest 6) 1012.Pinball(HDU 6373) -简单的计算几何+物理受力分析