关于基尔霍夫矩阵:
*算法引入:
*给定一个无向图G,求它生成树的个数t(G);
*
*算法思想:
*(1)G的度数矩阵D[G]是一个n*n的矩阵,并且满足:当i≠j时,dij=0;当i=j时,dij等于vi的度数;
*(2)G的邻接矩阵A[G]是一个n*n的矩阵,并且满足:如果vi,vj之间有边直接相连,则aij=1,否则为0;
*定义图G的Kirchhoff矩阵C[G]为C[G]=D[G]-A[G];
*Matrix-Tree定理:G的所有不同的生成树的个数等于其Kirchhoff矩阵C[G]任何一个n-1阶主子式的行列式的绝对值;
*所谓n-1阶主子式,就是对于r(1≤r≤n),将C[G]的第r行,第r列同时去掉后得到的新矩阵,用Cr[G]表示;
思路:此题要求删掉m-n 条边得到联通图的方案数,相当于m条边选n条,得到联通图的方案数。
如果是选n-1条,那就是得到一个生成树。n条相当于在树上再加一条边,也就是有一个环的树。
那么就要枚举环,把环缩点,然后用基尔霍夫矩阵计算。
/*************************************************************************
> File Name: hdu5304.cpp
> Author: TechMonster
> Mail: 928221136@qq.com
> Created Time: 五 7/ 8 15:07:09 2016
************************************************************************/
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<ctype.h>
#include<string>
#include<math.h>
#include<map>
#include<set>
#include<vector>
#include<queue>
#include<algorithm>
using namespace std;
template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b>a)a = b; }
template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b<a)a = b; }
#define ls (o<<1)
#define rs (o<<1|1)
#define MS(x,y) memset(x,y,sizeof(x))
#define MC(x, y) memcpy(x, y, sizeof(x))
typedef long long LL;
const int INF = 0x3f3f3f3f;
const int N = 50010;
const LL M = 998244353ll;
template <class T1, class T2>inline void gadd(T1 &a, T2 b) {a += b; if(a > M) a%=M;}
int n,m;
int mp[16][16],top;
LL sum[70000],dp[16][70000];
LL Pow(LL x, LL n)
{
LL ret = 1;
while(n)
{
if(n&1) ret=ret*x%M;
n>>=1;
x = x*x%M;
}
return ret;
}
LL Gauss(LL C[][20],int n)//计算n阶行列式的绝对值 % mod
{
LL ans=1ll;
int flag=1;//行列交换的次数
int i,j,k;
for(i=0;i<n;i++)
{
if(C[i][i]==0)
{
for(j=i+1;j<n;j++)
if(C[j][i])break;
if(j==n)return 0;//某列的值全是0的ans=0;
flag=!flag;
for(int k=i;k<n;k++)
swap(C[i][k],C[j][k]);//i和j行交换
}
ans=ans*C[i][i]%M;//对角线相乘
LL x = Pow(C[i][i],M-2);//x为逆元
for(k=i+1;k<n;k++)
C[i][k]=C[i][k]*x%M;
for(int j=i+1;j<n;j++)
for(int k=i+1;k<n;k++)
{
C[j][k]=(C[j][k]-C[j][i]*C[i][k])%M;
if(j==k)
C[j][k]=(C[j][k]+M)%M;
}
for(k=i+1;k<n;k++)
C[i][k]=C[i][k]*C[i][i]%M;
}
ans=(ans%M+M)%M;
if(flag) return ans;
else return M-ans;
}
int getst(int x) {for(int i = 0; i < n; ++i) if(x & 1<<i) return i;return -1;}
int getnum(int x) {int ret = 0;while(x) x-=x&-x,ret++; return ret;}
void getCircle()
{
MS(dp,0);MS(sum,0);
top = 1<<n;
for(int sta = 1; sta < top; ++sta)
{
int s = getst(sta), num = getnum(sta);
if(num == 1) dp[s][sta] = 1;
for(int e = s; e < n; ++e)
{
if(!dp[e][sta]) continue;
for(int e2 = s+1; e2 < n; e2++)
{
if(sta & 1<<e2 || !mp[e][e2]) continue;
gadd(dp[e2][sta | 1<<e2],dp[e][sta]);
}
if(num >= 3 && mp[e][s])
gadd(sum[sta],dp[e][sta]);
}
}
}
int id[16];
LL C[20][20];
void solve()
{
int u,v;
MS(mp,0);
for(int i = 0; i < m; ++i)
{
scanf("%d%d",&u,&v);
u--,v--;
mp[u][v] = mp[v][u] = 1;
}
getCircle();
int cnt;
LL ans = 0;
for(int sta = 0; sta < top; ++sta)
{
MS(C,0);
if(!sum[sta]) continue;
cnt = 1;
for(int i = 0; i < n; ++i)
if(sta & 1<<i) id[i] = 0;
else id[i] = cnt++;
for(int i = 0; i < n; ++i)
for(int j = 0; j < n; ++j)
{
if(id[i] != id[j] && mp[i][j])
{
C[id[i]][id[i]]++;
C[id[i]][id[j]]--;
}
}
gadd(ans,Gauss(C,cnt-1)*sum[sta]%M);
}
printf("%lld\n",ans*Pow(2,M-2)%M);
}
int main()
{
while(~scanf("%d%d",&n,&m))
solve();
return 0;
}