题意:一幅无向图有n个点m条边,求删去m-n条边图依然联通的方案数;
参考:http://blog.csdn.net/starry__night/article/details/46756587
http://www.cnblogs.com/AlpcFlagship/p/4675921.html
思路:剩下n条边,则可看做一棵生成树加一条边,有一个唯一的环;为基环外向图;
通过dp[s][v]保存路径找环,环缩点后图变为生成树;
通过基尔霍夫矩阵高斯消元生成树计数;
#include<iostream> #include<cmath> #include<cstring> #include<cstdio> #include<algorithm> using namespace std; const int mo=998244353; struct atom{ int x,y; }; atom operator - (const atom k1,const atom k2){ return (atom){(k1.x-k2.x+mo)%mo,(k1.y-k2.y+mo)%mo}; } atom operator * (const atom k1,const int k2){ return (atom){1ll*k1.x*k2%mo,1ll*k1.y*k2%mo}; } int x[20][20],n,m,A[20][20],dp[1<<16][17],f[1<<16],pd[20]; int gauss(int n){ int pd=1; n--; for (int i=1;i<=n;i++) for (int j=1;j<=n;j++) x[i][j]=(x[i][j]+mo)%mo; for (int i=1;i<=n;i++){ int r=0; for (int j=i;j<=n;j++) if (x[j][i]){r=j; break;} for (int j=1;j<=n;j++) swap(x[i][j],x[r][j]); if (r!=i) pd=-pd; for (int j=i+1;j<=n;j++){ int k1=x[i][i],k2=x[j][i]; atom xx=(atom){1,0},y=(atom){0,1}; while (k2){ int k4=k1/k2; atom z=y; y=xx-y*k4; xx=z; k4=k2; k2=k1%k2; k1=k4; pd=-pd; } for (int k=1;k<=n;k++){ int k3=x[i][k],k4=x[j][k]; x[i][k]=(1ll*xx.x*k3+1ll*xx.y*k4)%mo; x[j][k]=(1ll*y.x*k3+1ll*y.y*k4)%mo; } } } for (int i=1;i<=n;i++) pd=1ll*pd*x[i][i]%mo; return (pd+mo)%mo; } int solve(){ memset(A,0x00,sizeof A); for (;m;m--){ int k1,k2; scanf("%d%d",&k1,&k2); A[k1][k2]=1; A[k2][k1]=1; } memset(f,0x00,sizeof f); for (int s=1;s<=n;s++){ memset(dp,0x00,sizeof dp); dp[(1<<s-1)][s]=1; for (int i=1;i<(1<<n);i++) for (int j=1;j<=n;j++) if (dp[i][j]){ for (int k=s+1;k<=n;k++) if ((i&(1<<k-1))==0&&A[j][k]) dp[i|(1<<k-1)][k]=(dp[i|(1<<k-1)][k]+dp[i][j])%mo; if (A[j][s]) f[i]=(f[i]+dp[i][j])%mo; } } int ans=0; for (int i=1;i<=n;i++) for (int j=i+1;j<=n;j++) if (A[i][j]) f[(1<<i-1)|(1<<j-1)]=(f[(1<<i-1)|(1<<j-1)]-1+mo)%mo; int inv2=(mo+1)/2; for (int i=0;i<(1<<n);i++) f[i]=1ll*f[i]*inv2%mo; for (int now=0;now<(1<<n);now++) if (f[now]){ for (int i=1;i<=n;i++) pd[i]=0; for (int i=1;i<=n;i++) if (now&(1<<i-1)) pd[i]=1; int sign=1; for (int i=1;i<=n;i++) if (pd[i]==0) pd[i]=++sign; for (int i=1;i<=n;i++) for (int j=1;j<=n;j++) x[i][j]=0; for (int i=1;i<=n;i++) for (int j=1;j<=n;j++) if (A[i][j]&&pd[i]!=pd[j]){ x[pd[i]][pd[j]]--; x[pd[i]][pd[i]]++; } ans=(ans+1ll*f[now]*gauss(sign))%mo; } return ans; } int main(){ // freopen("data.in","r",stdin); // freopen("data.out","w",stdout); while (scanf("%d%d",&n,&m)!=EOF) printf("%d\n",solve()); return 0; }