BZOJ2560 串珠子

题意:给出n个点以及一个邻接矩阵,C[i][j]表示i号点和j好点间存在的无向边条数。现在两个点可以选择不连边或者选一条边连接,问:有多少种连边方案,使得n个点形成一个联通块。

分析:由于点数很小,所以可以考虑状压DP。f[cur]表示若只考虑cur中为1的点,使得它们彼此联通的方案数,最后答案即为f[11..11]。如何求f[cur]?直接求联通的方案数不好求,可是反过来我们却发现求不连通的方案简单得飞起~~由于不连通,所以当前点集若以其中一个定点u为参考,那么必定可以划分为{和u在一个联通块的点}、{不和u在一个联通块的点}。考虑枚举前一个集合,记其为last,后一个集合可以用cur^last表示出,记其为rest。那么很显然这时的方案数为f[last]*g[rest](g[rest]表示rest中的点以及彼此存在的边组成的小图有多少种连边方式)。这样我们把每一种last集合求得的方案累加起来即为当前不连通的方案数。最后用g[cur]减去所有不连通的方案即为f[cur]。我们预处理出g数组,然后直接推一遍f就可以啦!

#include 
#define ll long long
#define rep(i,j,k) for (i=j;i<=k;i++)
using namespace std;
const int N=16,mod=1e9+7;
int cur,S,n,i,j,c[N+1][N+1];
int f[1<0;j=(j-1)&S)
    	  f[cur]=(f[cur]-(ll)g[j]*f[cur^j]%mod+mod)%mod;
	}
    printf("%d\n",f[(1<


你可能感兴趣的:(容斥原理)