顯然狀態壓縮
然後,對於一個點集S,我們很容易求出這個點集可以形成的任意圖\(F_S\)
這個很容易預處理出來
然後呢,對於這個直接求聯通的方案書並不容易,但是,可以用總方案減去不連通的方案數。
不連通的方案視爲兩個點集,一個點集隨便,另一個點集必須聯通。
所以在預處理完了以後,我們首先要做的就是在當前集合裏剝出一個點作爲強制的聯通集合的點,然後枚舉這個集合剝離後的子集作爲隨意點,其子集的補集作爲聯通點集。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define int long long
using namespace std;
int n;
int ma[101][101];
int maxn=18;
int f[1<<18];
int dp[1<<18];
int g[1<<18];
int m=1000000007;
int cnt;
signed main(){
scanf("%d",&n);
cnt=(1<<n)-1;
for(int i=1;i<=n;++i){
for(int j=1;j<=n;++j){
scanf("%d",&ma[i][j]);
}
}
for(int k=0;k<=cnt;++k){
f[k]=1;
for(int i=1;i<=n;++i){
if(k&1<<i-1){
for(int j=i+1;j<=n;++j){
if(k&1<<j-1){
f[k]=f[k]*(ma[i][j]+1)%m;
f[k]%=m;
}
}
}
}
}
for(int k=1;k<=cnt;++k){
dp[k]=f[k];
int frm=k^(k&-k);
for(int i=frm;i;i=(i-1)&frm){
dp[k]=(dp[k]-f[i]*dp[i^k]%m+m)%m;
}
}
cout<<dp[cnt];
return 0;
}