卧薪尝胆,厚积薄发。
串珠子
Date: Tue Oct 02 17:44:37 CST 2018
In Category:
NoCategory
Description:
给一个无向图,点
$i$
和点
$j$
之间有
$c_{i,j}$
条边,问有多少种选边的方案使得图联通。
$1\leqslant n\leqslant16$
Solution:
连通图计数的经典套路,设
$F[S]$
表示集合
$S$
中的点可以不连通的方案数,
$G[S]$
表示必须联通的方案数,那可以用不一定联通的减去一定不联通的,不一定连通的方案数
$\begin{align}F[S]=\prod_{i,j\in S}(c_{i,j}+1)\end{align}$
,然后枚举第一个点所在集合有哪些点,注意为了防止重复必须枚举第一个点所在的集合,设为
$S'$
,剩下的可以连通也可以不连通,但第一个点所在集合必须全部连通,所以
$G[S]=F[S]-G[S']\times F[\complement_SS']$
。
Code:
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;
#define MAXN 17
int n;
int f[1 << MAXN],g[1 << MAXN];
#define MOD 1000000007
int c[MAXN][MAXN];
int main()
{
scanf("%d",&n);
for(int i = 1;i <= n;++i)
for(int j = 1;j <= n;++j)
scanf("%d",&c[i][j]);
int tot = (1 << n) - 1;
for(int b = 0;b <= tot;++b)f[b] = 1;
for(int b = 0;b <= tot;++b)
for(int i = 1;i <= n;++i)
for(int j = i + 1;j <= n;++j)
if((b & (1 << (i - 1))) && (b & (1 << (j - 1))))
f[b] = 1ll * f[b] * (c[i][j] + 1) % MOD;
for(int b = 0;b <= tot;++b)
{
g[b] = f[b];
for(int k = b;k != 0;k = (k - 1) & b)
{
if((k & 1) == 0)continue;
if(k == b)continue;
g[b] = (g[b] - 1ll * g[k] * f[b ^ k] % MOD + MOD) % MOD;
}
}
cout << g[tot] << endl;
return 0;
}
Copyright © 2020
wjh15101051
ღゝ◡╹)ノ♡