题目:http://acm.hdu.edu.cn/showproblem.php?pid=5304
16个点的无向图,问能生成多少个n条边的连通图。(即多一条边的树)
先n^3 * 2^n 枚举全部的环。状压dp即可。dp[i][j]表示以i为终点,走了j状态集合的方案数。要枚举起点,每次走比起点大的点。所以要n^3 2^n枚举。
把环压缩成点。构造基尔霍夫矩阵。每种状态下n^3求生成树。
故总复杂度是 n^3 * 2^n + n^3 * 2^n
代码:
#include<iostream> #include<cstdio> #include<algorithm> #include<vector> #include<cstring> using namespace std; typedef long long ll; const ll mod = 998244353; const int N = (1<<16)+10; ll ans; int f[N],dp[20][N]; bool d[20][20]; int n,m; ll exgcd(ll a,ll b,ll &x,ll &y)//乘法逆元返回的d是a,b的公约数。x是a mod b的逆元 { if(b==0) { x=1ll;y=0; return a; } ll d=exgcd(b,a%b,x,y); ll t=x; x=y; y=t-a/b*y; return d; } ll Gauss(int 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]%mod;//对角线相乘 ll x,y; int tp=exgcd(C[i][i],mod,x,y);//x为逆元 for(k=i+1;k<n;k++) C[i][k]=C[i][k]*x%mod; for(int j=i+1;j<n;j++) for(int k=i+1;k<n;k++) { C[j][k]=(C[j][k]-(ll)C[j][i]*C[i][k])%mod; if(j==k) C[j][k]=(C[j][k]+mod)%mod; } for(k=i+1;k<n;k++) C[i][k]=(ll)C[i][k]*C[i][i]%mod; } ans=(ans%mod+mod)%mod; if(flag) return ans; else return mod-ans; } ll solve(int s){ int Kir[20][20]; int vis[20],col[20]; memset(Kir,0,sizeof(Kir)); memset(vis,0,sizeof(vis)); memset(col,-1,sizeof(col)); for(int i=0;i<n;i++) if((1<<i)&s) vis[i]=1; int num=0; for(int i=0;i<n;i++)if(!vis[i]) col[i]=num++; for(int i=0;i<n;i++)if(vis[i]) col[i]=num; num++; for(int i=0;i<n;i++) for(int j=0;j<n;j++)if(i!=j && col[i]!=col[j]) Kir[col[i]][col[j]] -= d[i][j]; for(int i=0;i<n;i++) for(int j=0;j<n;j++)if(col[i]!=col[j]) Kir[col[i]][col[i]] += d[i][j]; return Gauss(Kir,num-1); } int main(){ // freopen("e.in","r",stdin); // freopen("my05.out","w",stdout); while(scanf("%d%d",&n,&m)!=EOF){ ans = 0; memset(d,0,sizeof(d)); for(int i=1;i<=m;i++){ int u,v; scanf("%d%d",&u,&v); u--,v--; d[u][v] = d[v][u] = 1; } memset(f,0,sizeof(f)); for(int st=0;st<n;st++){ memset(dp,0,sizeof(dp)); dp[st][(1<<st)] = 1; for(int s=1;s<(1<<n);s++)if((1<<st)&s){ for(int i=0;i<n;i++)if(i>=st) if(dp[i][s]){ for(int j=st+1;j<n;j++) if( (s&(1<<j))==0 && d[i][j] ) dp[j][s|(1<<j)] = ( dp[i][s] + dp[j][s|(1<<j)])%mod; if(d[i][st]) f[s] = ( dp[i][s] + f[s] )%mod; } } } ll inv2 = (mod+1)/2; for(int i=1;i<(1<<n);i++){ if(f[i]){ int co = 0; for(int j=0;j<n;j++)if((1<<j)&i) co++; if(co<=2) continue; f[i] = ((ll)f[i]*inv2)%mod; ans = (ans + (ll)f[i]*solve(i)%mod )%mod; } } printf("%I64d ",ans); } return 0; }