1. 程式人生 > 實用技巧 >2020杭電HDU-6836多校第六場Expectation(矩陣樹及其注意事項)

2020杭電HDU-6836多校第六場Expectation(矩陣樹及其注意事項)

題目連結:http://acm.hdu.edu.cn/showproblem.php?pid=6836
CSDN食用連結:https://blog.csdn.net/qq_43906000/article/details/107850424

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}\ mod\; 998244353\)

where \(x×y^{−1}\) is the irreducible fraction representation of the result, where \(y^{−1}\) 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)\)and \(m(1≤m≤10^4)\)

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≤10^9,u≠v)\), space separated, denoting an weight edge between \(u\) and \(v\) has weight \(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\),定義樹的權為樹的所有邊的邊權的按位與。現在我們隨機選擇該圖的一個生成樹,問其生成樹的權期望是多少。

emmm,一眼就是矩陣樹。。。但中間的邊權就真的卡的死死的。。。不會算,後來看題解才知道的。

每一位的按位與過程都是獨立的,那麼我們對每一位建立一個其含該位的邊的基爾霍夫矩陣,然後就可以得出在該位下可以得到多少個生成樹,那麼他對答案的貢獻就是\(ans*\frac{2^i}{sum}\)其中\(i\)表示是第幾位,\(ans\)表示的是在該位下的生成樹個數,\(sum\)表示的是總的生成樹的個數。

然後就可以開始跑矩陣樹了,只不過需要注意的是,矩陣樹的的高斯消元和一般的高斯消元不太一樣,他是從2開始的!!!(一般的是從1開始的,然後很多時候就會得到0)這裡就被卡了挺久的。接下來需要注意的是,100個點,1W條邊,就算是完全圖也沒有這麼多條邊,所以他一定會存在重邊,那麼我們用vector儲存就好了。

以下是AC程式碼:

#include <bits/stdc++.h>
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;
}