题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2560
和地震后的幻想乡思路一样,枚举点集。
因为求不连通容易,所以容斥一下,用全集减去不连通的。
也还是为了不重不漏枚举一个划分点。
哎呀真是一模一样呢!
1.理解更深的一点:划分点的必要性。
比如点集{1,2,3,4},如果不枚举划分点,减掉的东西里:1)当 f [ 1,2 ] 和 g [ 3,4 ] 的时候,含一个 f 1,2 ] x f [ 3,4 ],
2)当 f [ 3,4 ] 和 g [ 1,2 ] 的时候,含一个 f [ 1,2 ] x f [ 3,4 ];
所以必须枚举划分点。
2.深刻的不明白的问题!
发现如果指定连通的子集中必须包含划分点,就没问题;可是如果指定成连通的子集中必须不包含划分点,就错误。这是为什么?
这都是因为那种 只有自己一个点还算联通 的情况太特殊了!
考虑反例:其实就是会把那种图中一条边都没有的情况减好多遍。这种感觉。
#include<iostream> #include<cstdio> #include<cstring> #define ll long long using namespace std; const int N=16;const ll mod=1e9+7; int n,c[N+5][N+5],lm; ll f[(1<<N)+5],g[(1<<N)+5]; int main() { scanf("%d",&n);lm=(1<<n); for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)scanf("%d",&c[i][j]); for(int s=1;s<lm;s++) { g[s]=1; for(int i=1;i<=n;i++)if(s&(1<<(i-1))) for(int j=i+1;j<=n;j++)if(s&(1<<(j-1))) (g[s]*=c[i][j]+1)%=mod; f[s]=g[s]; int k; for(k=1;k<=n&&(s&(1<<(k-1)))==0;k++); // for(int j=((s-1)&s);j;j=((j-1)&s))if(j&(1<<(k-1))) //50 // (f[s]-=f[j]*g[s^j])%=mod; k=(s^(1<<(k-1))); //50 for(int j=k;j;j=((j-1)&k))f[s]=((f[s]-g[j]*f[s^j])%mod+mod)%mod;//不是&s! // for(int j=((s-1)&s);j;j=((j-1)&s))if((j&(1<<(k-1)))==0)//49 // (f[s]-=f[j]*g[s^j])%=mod; } printf("%lld",f[lm-1]); return 0; }