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(x31231+x30230+...+x020)=E(x31231)+E(x30230)+...+E(x020)

下面我们只要考虑每一位就可以了。

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(xi2i)=2isumnum

依次计算32位,最后相加就行了。

然后生成树个数的计算,使用矩阵树定理就能计算出来。

矩阵树定理:

矩阵树定理参考博客地址
百度百科:

求出基尔霍夫矩阵之后,只要求它的一个代数余子式即可。下面的模板是求 a n n a_nn ann的代数余子式,即 ( − 1 ) n + n ∗ M n n (-1)^n + n*M_nn (1)n+nMnn,因为 n + n n + n n+n一定是偶数,所以也就是求去掉第 n n n行,第 n n n列之后的 n − 1 n-1 n1阶方阵的行列式。

行列式是转换成上三角之后,对角线元素乘积直接得到。

:高斯消元部分模板是采用辗转相除的方式消去下面的行,因为答案要取模。与求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

hdu多校第六场1008 (hdu6641)TDL 暴力

多校第六场 1003 hdu 5355 Cake(贪心)

2019HDU多校第六场1009 Three Investigators——杨表

2019杭电多校第六场hdu6638 Snowy Smile(线段树+枚举)

杭电2018多校第六场(2018 Multi-University Training Contest 6) 1012.Pinball(HDU 6373) -简单的计算几何+物理受力分析