f[s]f[s]f[s]表示集合sss中的每个点都与111号点连通的方案数,
g[s]g[s]g[s]表示集合sss中的点任意连边的方案总数,包括不连边的情况。
容易得到g[s]g[s]g[s]的递推关系(因为包括了不连边的情况所以要在c[i][j]c[i][j]c[i][j]后面+1+1+1):
g[s]=∏i,j∈s(c[i][j]+1)g[s]=\prod _{i,j∈s}(c[i][j]+1)g[s]=i,j∈s∏(c[i][j]+1)
接下来通过容斥原理考虑f[s]f[s]f[s]的递推关系。
显然f[s]f[s]f[s]应当由g[s]g[s]g[s]递推而来,但是g[s]g[s]g[s]有许多多余的情况,即与111号点没有连通的方案总数。
设s′s's′为sss的子集,s′′s''s′′为s′s's′在sss中的补集,若s′s's′状态中不包括111号点,那么s′′s''s′′状态中一定包括了111号点,所以应当减去f[s′′]∗g[s′]f[s'']*g[s']f[s′′]∗g[s′],于是我们得到f[s]f[s]f[s]的递推关系:
f[s]=g[s]−∑(f[s′′]∗g[s′])f[s]=g[s]-\sum (f[s'']*g[s'])f[s]=g[s]−∑(f[s′′]∗g[s′])
其中s′,s′′s',s''s′,s′′的意义参见上文或代码:
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define ll long long
#define rep(i,x,y) for(ll i=(x);i<=(y);i++)
#define repl(i,x,y) for(ll i=(x);i<(y);i++)
#define repd(i,x,y) for(ll i=(x);i>=(y);i--)
using namespace std;
const ll N=16;
const ll Mod=1e9+7;
ll f[1<<N],g[1<<N];
ll n,all,c[N][N];
inline ll read() {
ll x=0;char ch=getchar();bool f=0;
while(ch>'9'||ch<'0'){if(ch=='-')f=1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return f?-x:x;
}
inline ll add(ll x,ll y) {return x+y>=Mod?x+y-Mod:x+y;}
inline ll sub(ll x,ll y) {return x-y<=0?x-y+Mod:x-y;}
inline ll mul(ll x,ll y) {return x*y%Mod;}
int main() {
n=read();all=(1<<n)-1;
repl(i,0,n) repl(j,0,n) c[i][j]=read();
rep(s,1,all) {
g[s]=1;
repl(i,0,n) if(s&(1<<i)) repl(j,i+1,n) if(s&(1<<j)) g[s]=mul(g[s],c[i][j]+1);
if(s&1) {
f[s]=g[s];
for(ll s1=s;s1;s1=(s1-1)&s) if(!(s1&1)) f[s]=sub(f[s],mul(g[s1],f[s^s1]));
}
}
printf("%lld",f[all]);
return 0;
}
图论算法详解

本文深入探讨了图论中的两种关键算法:f[s] 和 g[s]。f[s] 计算的是图中所有与特定点连接的连通方案数量,而 g[s] 则涵盖了所有可能的边连接方案,包括不连边的情况。文章详细解析了这两种算法的递归关系,通过容斥原理优化 f[s] 的计算,并提供了一段 C++ 实现代码。
401

被折叠的 条评论
为什么被折叠?



