题目描述:
已知所有珠子互不相同,用整数1到n编号。对于第i个珠子和第j个珠子,可以选择不用绳子连接,或者在ci,j根不同颜色的绳子中选择一根将它们连接。如果把珠子看作点,把绳子看作边,将所有珠子连成一个整体即为所有点构成一个连通图。特别地,珠子不能和自己连接。
铭铭希望知道总共有多少种不同的方案将所有珠子连成一个整体。由于答案可能很大,因此只需输出答案对1000000007取模的结果。
题目分析:
用
f
[
s
]
f[s]
f[s]表示s状态中为1的珠子连通的方案数。
如果只用f来正着递推,发现很自闭,怎么都会重。
但是如果算不连通的方案数就很好算了,只需要枚举其中一个点所在连通块,另外的点任意连边,这个是可以直接算的。
所以连通方案数=总方案减去不连通的。
用
g
[
s
]
g[s]
g[s]表示其中的点任意连边的方案数,那么有
f
[
s
]
=
g
[
s
]
−
∑
t
属
于
(
s
去
掉
最
低
位
)
g
[
t
]
∗
f
[
s
−
t
]
f[s]=g[s]-\sum_{t属于(s去掉最低位)}g[t]*f[s-t]
f[s]=g[s]−∑t属于(s去掉最低位)g[t]∗f[s−t]。
Code:
#include<cstdio>
#include<cctype>
#include<algorithm>
#define maxn 16
using namespace std;
char cb[1<<18],*cs,*ct;
#define getc() (cs==ct&&(ct=(cs=cb)+fread(cb,1,1<<18,stdin),cs==ct)?0:*cs++)
inline void read(int &a){
char c;bool f=0;while(!isdigit(c=getc())) c=='-'&&(f=1);
for(a=c-'0';isdigit(c=getc());a=a*10+c-'0'); f&&(a=-a);
}
const int mod = 1e9+7;
int n,c[maxn][maxn],f[1<<maxn],g[1<<maxn],lg[1<<maxn];
int main()
{
#ifndef ONLINE_JUDGE
freopen("1.in","r",stdin);
#endif
read(n);
for(int i=0;i<n;i++) for(int j=0;j<n;j++) read(c[i][j]);
g[0]=1; for(int i=0;i<n;i++) lg[1<<i]=i;
for(int s=1,pre;s<1<<n;s++){
g[s]=g[pre=s^(s&-s)];
for(int t=pre,i=lg[s&-s];t;t^=t&-t) g[s]=1ll*g[s]*(c[i][lg[t&-t]]+1)%mod;
f[s]=g[s];
for(int t=pre;t;t=(t-1)&pre) f[s]=(f[s]-1ll*g[t]*f[s^t])%mod;
}
printf("%d\n",(f[(1<<n)-1]+mod)%mod);
}