正题
题目链接:https://www.luogu.com.cn/problem/P5933
题目大意
nnn个点的一张无向图,求所有联通子图的权值乘积和
解题思路
因为nnn很小,考虑状压
设fif_ifi表示点集为iii时的方案数,我们发现正着做十分麻烦,考虑容斥。
首先定义mulimul_imuli表示点集iii的所有子图的方案数,那么显然有muli=∏x,y∈i,x<y(ax,y+1)mul_i=\prod^{x,y\in i,x<y}(a_{x,y}+1)muli=∏x,y∈i,x<y(ax,y+1)
枚举一个集合sss有转移方程,选出一个点ppp一定在联通图内,这里状压为只有一个点ppp的集合fs=muls−∑i∈(s−p)fi+p∗muls−p−if_s=mul_s-\sum_{i\in (s-p)}f_{i+p}*mul_{s-p-i}fs=muls−i∈(s−p)∑fi+p∗muls−p−i
时间复杂度O(3n)O(3^n)O(3n)
codecodecode
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const ll N=17,XJQ=1e9+7;
ll n,mul[1<<N],f[1<<N],a[N][N];
int main()
{scanf("%lld",&n);for(ll i=0;i<n;i++)for(ll j=0;j<n;j++)scanf("%lld",&a[i][j]);ll MS=1<<n;for(ll s=0;s<MS;s++){mul[s]=1;for(ll i=0;i<n;i++){if(!((s>>i)&1))continue;mul[s]=mul[s^(1<<i)];for(ll j=i+1;j<n;j++)if(((s>>i)&1)&&((s>>j)&1))mul[s]=mul[s]*(a[i][j]+1)%XJQ;break;}}mul[0]=0;for(ll s=1;s<MS;s++){f[s]=mul[s];ll p=(s&-s),i=s-p;for(ll j=i;j>=0;j=(j==0?-1:((j-1)&i)))f[s]=(f[s]-f[j+p]*mul[i-j]%XJQ+XJQ)%XJQ;}printf("%lld",f[MS-1]);
}