Expectation(生成树计数)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6836
题意:给你 n 个点 m 条边的图,按照输入顺序第 i 条边的权值为 2^i,生成树的权值为树上所有边的权值的二进制与运算,随机选择一颗生成树,求生成树权值的期望。
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≤104) 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≤109,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
Sample Output
1
思路:

#include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<algorithm> #include<queue> #include<stack> #include<vector> #include<map> #include<set> #define inf 0x3f3f3f3f using namespace std; typedef long long ll; const int N = 110; const double eps = 1e-8; const ll mod = 998244353; struct node { int from, to; ll w; } edge[10010]; int n, m; ll ka[N][N]; ll pow2[35]; ll gauss(int n) { ll res = 1; for(int i = 1; i <= n - 1; i++) { for(int j = i + 1; j <= n - 1; j++) { while(ka[j][i]) { int t= ka[i][i] / ka[j][i]; for(int k = i; k <= n - 1; k++) { ka[i][k] = (ka[i][k] - t * ka[j][k] + mod) % mod; } swap(ka[i], ka[j]); res = -res; } } res = (res * ka[i][i]) % mod; } return (res + mod) % mod; } ll qpow(ll a, ll n) { ll ans = 1; while(n) { if(n & 1) ans = ans * a % mod; a = a * a % mod; n >>= 1; } return ans % mod; } ll inv(ll x) { return qpow(x, mod - 2) % mod; } int main() { pow2[0] = 1; for(int i = 1; i < 35; i++) { pow2[i] = pow2[i - 1] * 2; } int t; scanf("%d", &t); while(t--) { scanf("%d %d", &n, &m); memset(ka, 0, sizeof(ka)); for(int i = 1; i <= m; i++) { scanf("%d %d %lld", &edge[i].from, &edge[i].to, &edge[i].w); int u = edge[i].from; int v = edge[i].to; ka[u][u]++; ka[v][v]++; ka[u][v]--; ka[v][u]--; } ll tot = gauss(n); ll fin = 0; for(int bt = 0; bt <= 30; bt++) { memset(ka, 0, sizeof(ka)); for(int i = 1; i <= m; i++) { int u = edge[i].from; int v = edge[i].to; ll w = edge[i].w; if(w & pow2[bt]) { ka[u][u]++; ka[v][v]++; ka[u][v]--; ka[v][u]--; } } ll ans = gauss(n); fin += pow2[bt] * ans % mod; fin %= mod; } fin *= inv(tot); fin %= mod; printf("%lld\n", fin); } return 0; }
学习博客:https://blog.csdn.net/u011815404/article/details/99679527
对于生成树的计数,一般采用矩阵树定理(Matrix-Tree 定理)来解决。
Matrix-Tree 定理的内容为:对于已经得出的基尔霍夫矩阵,去掉其随意一行一列得出的矩阵的行列式,其绝对值为生成树的个数
因此,对于给定的图 G,若要求其生成树个数,可以先求其基尔霍夫矩阵,然后随意取其任意一个 n-1 阶行列式,然后求出行列式的值,其绝对值就是这个图中生成树的个数。
LL K[N][N]; LL gauss(int n){//求矩阵K的n-1阶顺序主子式 LL res=1; for(int i=1;i<=n-1;i++){//枚举主对角线上第i个元素 for(int j=i+1;j<=n-1;j++){//枚举剩下的行 while(K[j][i]){//辗转相除 int 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]);//交换i、j两行 res=-res;//取负 } } res=(res*K[i][i])%MOD; } return (res+MOD)%MOD; } int main(){ int n,m; scanf("%d%d",&n,&m); memset(K,0,sizeof(K)); for(int i=1;i<=m;i++){ int x,y; scanf("%d%d",&x,&y); K[x][x]++; K[y][y]++; K[x][y]--; K[y][x]--; } printf("%lld\n",gauss(n)); return 0; }
kuangbin模板:

浙公网安备 33010602011771号