2020杭电HDU-6836多校第六场Expectation(矩阵树及其注意事项)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6836
博客园食用链接:https://www.cnblogs.com/lonely-wind-/p/13449312.html

You are given an undirected graph consisting of n vertices with m weighted edges. We define the weight of a spanning tree as the bitwise AND of all edges’ weight in spanning tree.

Now select a spanning tree randomly, you should calculate the expected value of the weight of this spanning tree. You are required to print the result mod 998244353. i.e., print x × y − 1   m o d    998244353 x×y^{−1}\ mod\; 998244353 x×y1 mod998244353 where x × y − 1 x×y^{−1} x×y1 is the irreducible fraction representation of the result, where y − 1 y^{−1} y1 denotes the multiplicative inverse of y modulo 998244353.

Input
The first line is an integer t(1≤t≤10), the number of test cases.

For each test case, there are two space-separated integers n ( 2 ≤ n ≤ 100 ) n(2≤n≤100) n(2n100)and m ( 1 ≤ m ≤ 1 0 4 ) m(1≤m≤10^4) m(1m104) in the first line, the number of nodes and the number of edges.

Then follows m lines, each contains three integers u , v , w ( 1 ≤ u , v , ≤ n , 1 ≤ w ≤ 1 0 9 , u ≠ v ) u,v,w(1≤u,v,≤n,1≤w≤10^9,u≠v) u,v,w(1u,v,n,1w109,u=v), space separated, denoting an weight edge between u u u and v v v has weight w w w.

Output
For each test case, output a single line with a single integer, denoting the answer.

Sample Input
1
3 3
1 2 1
1 3 1
2 3 1
Sample Output
1

题目大意:给你一个无向图,其中n个点,m条边,每个边的边权为 w i w_i wi,定义树的权为树的所有边的边权的按位与。现在我们随机选择该图的一个生成树,问其生成树的权期望是多少。

emmm,一眼就是矩阵树。。。但中间的边权就真的卡的死死的。。。不会算,后来看题解才知道的。

每一位的按位与过程都是独立的,那么我们对每一位建立一个其含该位的边的基尔霍夫矩阵,然后就可以得出在该位下可以得到多少个生成树,那么他对答案的贡献就是 a n s ∗ 2 i s u m ans*\frac{2^i}{sum} anssum2i其中 i i i表示是第几位, a n s ans ans表示的是在该位下的生成树个数, s u m sum sum表示的是总的生成树的个数。

然后就可以开始跑矩阵树了,只不过需要注意的是,矩阵树的的高斯消元和一般的高斯消元不太一样,他是从2开始的!!!(一般的是从1开始的,然后很多时候就会得到0)这里就被卡了挺久的。接下来需要注意的是,100个点,1W条边,就算是完全图也没有这么多条边,所以他一定会存在重边,那么我们用vector保存就好了。

以下是AC代码:

#include 
using namespace std;

typedef long long ll;
const int mac=200;
const int mod=998244353;

vector<int>mp[mac][mac];
ll mat[mac][mac];
int n,m;

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

ll guass()
{
    ll ans=1;
    for (int i=2; i<=n; i++){
        for (int j=i+1; j<=n; j++)
            while (mat[j][i]){
                ll p=mat[i][i]/mat[j][i];
                for (int k=i; k<=n; k++)
                    mat[i][k]=(mat[i][k]-mat[j][k]*p%mod+mod)%mod;
                swap(mat[i],mat[j]);
                ans=-ans;
            }
        ans=ans*mat[i][i]%mod;
    }
    return (ans+mod)%mod;
}

int main(int argc, char const *argv[])
{
    int t;
    scanf ("%d",&t);
    while (t--){
        scanf ("%d%d",&n,&m);
        for (int i=1; i<=m; i++){
            int u,v,w;
            scanf ("%d%d%d",&u,&v,&w);
            mp[u][v].push_back(w); mp[v][u].push_back(w);
            mat[u][v]--; mat[v][u]--;
            mat[u][u]++; mat[v][v]++;
        }
        ll ans=qpow(guass(),mod-2);
        ll fz=0;
        for (int wei=0; wei<=30; wei++){
            memset(mat,0,sizeof mat);
            for (int i=1; i<=n; i++)
                for (int j=i+1; j<=n; j++){
                    for (auto x:mp[i][j]){
                        if (x&(1<<wei)){
                            mat[i][j]--; mat[j][i]--; 
                            mat[i][i]++; mat[j][j]++;
                        }
                    }
                }
            fz=(fz+qpow(2,wei)*guass()%mod)%mod;
        }
        memset(mat,0,sizeof mat);
        for (int i=1; i<=n; i++)
            for (int j=1; j<=n; j++) mp[i][j].clear();
        printf("%lld\n",ans*fz%mod);
    }
    return 0;
}

你可能感兴趣的:(#,矩阵树,HDU,多校,矩阵树)