首页 > 技术文章 > BZOJ2560串珠子

luoyibujue 2019-03-10 17:06 原文

/*
很清新的一道题(相比上一道题)
g[S]表示该 S集合中胡乱连的所有方案数, f[S] 表示S集合的答案
那么F[S] 等于G[S]减去不合法的部分方案
不合法的方案就枚举合法的部分就好了

 g[S]求法可以由选择一个点和其他没被选择的之间连边的

*/
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<queue>
#include<cmath>
#define ll long long
#define M 16
#define mmp make_pair
using namespace std;
const int mod = 1000000007;
void add(int &x, int y) {
	x += y;
	x -= x >= mod ? mod : 0;
	x += x < 0 ? mod : 0;
}

int read() {
	int nm = 0, f = 1;
	char c = getchar();
	for(; !isdigit(c); c = getchar()) if(c == '-') f = -1;
	for(; isdigit(c); c = getchar()) nm = nm * 10 + c - '0';
	return nm * f;
}
int poww(int a, int b) {
	int ans = 1, tmp = a;
	for(; b; b >>= 1, tmp = 1ll * tmp * tmp % mod) if(b & 1) ans = 1ll * ans * tmp % mod;
	return ans;
}

int f[1 << M], g[1 << M], a[M][M], n;

int main() {
	n = read();
	for(int i = 0; i < n; i++) {
		for(int j = 0; j < n; j++) {
			a[i][j] = read();
		}
	}
	f[0] = g[0] = 1;
	for(int i = 1; i < (1 << n); i++) {
		int x = -1, ans = 1;
		for(int j = 0; j < n; j++) {
			if((i & (1 << j)) == 0) continue;
			if(x == -1) x = j;
			else {
				ans = 1ll * ans * (a[x][j] + 1) % mod;
			}
		}
		g[i] = 1ll * g[i ^ (1 << x)] * ans % mod;
	}
	for(int i = 1; i < (1 << n); i++) {
		f[i] = g[i];
		int k = i ^ (i & -i);
		for(int j = k; j; j = (j - 1) & k) {
			add(f[i], -1ll * g[j] * f[i ^ j] % mod);
		}
	}
	cout << f[(1 << n) - 1] << "\n";
	return 0;
}


推荐阅读