2020杭电多校 6J / HDU 6836 - Expectation (数学、计数)

HDU 6836 - Expectation


题意

定义一颗生成树的值为所有边权按位与的答案

给定一张图,求生成树的值的期望




思路

既然是取按位与后的值作为权值,那么可以从二进制的方向考虑答案

假如对于某一棵生成树,它的答案在二进制的第\(i\)位上不为\(0\)

那也就等同于这棵生成树的所有边的边权第\(i\)位都不为\(0\)

所以可以考虑二进制上每一位对答案的贡献

先使用题目给定的所有边进行一次生成树计数,表示总共可以生成多少棵不同的生成树,记作\(tot\)

然后考虑二进制上的右数第\(i\)位(表示\(2^{i-1}\)

既然要使得答案的第\(i\)位为\(1\),上面说过,所有边边权第\(i\)位都必须为\(1\)

所以我们遍历一遍所有边,将第\(i\)位为\(1\)的边作为可行边

用第\(i\)位所有的可行边再做生成树计数,将此时生成数的数量记作\(cnt_i\)

那么第\(i\)位在答案中为\(1\)的概率就是\(\frac{cnt_i}{tot}\)

又因为第\(i\)位对答案的贡献为\(2^{i-1}\)

所以数学期望为\(2^{i-1}\frac{cnt_i}{tot}\)

由于题目范围有\(10^9\),所以大概要考虑到\(2^{30}\)

所以最终答案即为\(\sum_{i=0}^{30}2^{i-1}\frac{cnt_i}{tot}\)

(对于生成树计数,直接套板子即可,代码里的板子是使用这篇博客的:《图论 —— 生成树 —— 生成树计数》




代码

(265ms/5000ms)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=998244353;
const int N=105,M=10050;

int x[M],y[M],v[M];
ll K1[N][N],K2[N][N];

ll qpow(ll a,ll n)
{
    ll r=1;
    while(n)
    {
        if(n&1)
            r=(r*a)%mod;
        n>>=1;
        a=(a*a)%mod;
    }
    return r;
}

ll gauss(int n,ll K[N][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 t=K[i][i]/K[j][i];
                for(int k=i;k<=n-1;k++)
                    K[i][k]=(K[i][k]-t*K[j][k]+mod)%mod;
                swap(K[i],K[j]);
                res=-res;
            }
        }
        res=(res*K[i][i])%mod;
    }
    return (res+mod)%mod;
}

void solve()
{
    int n,m;
    cin>>n>>m;
    memset(K1,0,sizeof K1);
    for(int i=1;i<=m;i++)
    {
        cin>>x[i]>>y[i]>>v[i];
        K1[x[i]][x[i]]++;
        K1[y[i]][y[i]]++;
        K1[x[i]][y[i]]--;
        K1[y[i]][x[i]]--;
    }
    ll d=gauss(n,K1),invd=qpow(d,mod-2),ans=0; //计算逆元
    for(int i=0;i<=30;i++)
    {
        memset(K2,0,sizeof K2);
        ll tmp=1<<i;
        for(int j=1;j<=m;j++)
        {
            if(v[j]&tmp)
            {
                K2[x[j]][x[j]]++;
                K2[y[j]][y[j]]++;
                K2[x[j]][y[j]]--;
                K2[y[j]][x[j]]--;
            }
        }
        ans=(ans+tmp*gauss(n,K2)%mod*invd%mod)%mod; //权值*概率=期望
    }
    cout<<ans<<'\n';
}
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    int T;cin>>T;
    while(T--)
        solve();
    return 0;
}

posted @ 2020-08-06 19:05  StelaYuri  阅读(192)  评论(1编辑  收藏  举报