题意:
铭铭有n个十分漂亮的珠子和若干根颜色不同的绳子。现在铭铭想用绳子把所有的珠子连接成一个整体。
现在已知所有珠子互不相同,用整数1到n编号。对于第i个珠子和第j个珠子,可以选择不用绳子连接,或者在c[i,j]根不同颜色的绳子中选择一根将它们连接。
如果把珠子看作点,把绳子看作边,将所有珠子连成一个整体即为所有点构成一个连通图。特别地,珠子不能和自己连接。
铭铭希望知道总共有多少种不同的方案将所有珠子连成一个整体。由于答案可能很大,因此只需输出答案对1000000007取模的结果。
n<=16,a[i][j]<=1e9+7
思路:
还记得n个点有标号的无向连通图个数怎么求吗?如果记得的话,此题就简单了。
用f[S]表示与1号点连通的点的状态为S的方案数。我们先与处理出g数组,g[S]=∏u,v∈S(c[u][v]+1),然后f[S]就等于g[S]减去S中某些点与1号点不连通的方案数。
那么我们枚举此时与1号点连通的点的状态,其余的点与这个连通块均没有边相连,但是其余的点之间可以任意连边,
所以有:
f[S]=∑S′⊊Sf[S′]×g[S−S′]
所以时间复杂度就是枚举子集的O(3^n)
需要注意的是k=log(x)/log(2)+1这种写法似乎在BZOJ上不能用,需要预处理或者暴力
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<map>
#include<set>
#include<cmath>
using namespace std;
long long dp[<<],f[<<];
long long a[][];
const int MOD=;
int n; int lowbit(int x)
{
return (x&(-x));
} int who(int x)
{
int s=;
int k=x;
while(k)
{
s++;
k>>=;
}
return s;
} int main()
{ scanf("%d",&n);
for(int i=;i<=n;i++)
for(int j=;j<=n;j++) scanf("%lld",&a[i][j]);
f[]=; int M=(<<n)-;
for(int i=;i<=M;i++)
{
int x=lowbit(i);
int y=who(x);
f[i]=f[i-x];
for(int j=;j<=n;j++)
if((y!=j)&&(i&(<<(j-)))) f[i]=f[i]*(a[y][j]+)%MOD;
} dp[]=;
for(int i=;i<=M;i++)
if(i&)
{
int v=(i-);
dp[i]=f[i];
while(v)
{
dp[i]=dp[i]-(f[v]*dp[i-v])%MOD;
dp[i]=(dp[i]%MOD+MOD)%MOD;
v=((i-)&(v-));
}
}
printf("%lld\n",dp[M]);
return ;
}