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