• 2020杭电多校 6J / HDU 6836


    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<<'
    ';
    }
    int main()
    {
        ios::sync_with_stdio(0);
        cin.tie(0);cout.tie(0);
        int T;cin>>T;
        while(T--)
            solve();
        return 0;
    }
    

  • 相关阅读:
    【Linux】【jenkins】自动化部署一 安装jenkins及Jenkins工作目录迁移
    【Linux】【docker】docker私服安装
    【Linux】【docker】docker及docker-compose安装
    【Linux】【tomcat】tomcat8.5安装
    【Linux】【jdk】jdk8.0安装
    【Linux】【mysql】mysql8.0开启远程访问及常见问题
    【Linux】记录一个yum update和upgrade的区别
    【Linux】【gitlab】gitlab安装、备份、恢复、升级、内存消耗问题
    Python序列——列表
    Python序列——元组
  • 原文地址:https://www.cnblogs.com/stelayuri/p/13448265.html
Copyright © 2020-2023  润新知