bzoj 4671 异或图

Description

定义两个结点数相同的图 G1 与图 G2 的异或为一个新的图 G, 其中如果 (u, v) 在 G1 与
G2 中的出现次数之和为 1, 那么边 (u, v) 在 G 中, 否则这条边不在 G 中.
现在给定 s 个结点数相同的图 G1...s, 设 S = {G1, G2, . . . , Gs}, 请问 S 有多少个子集的异
或为一个连通图?

Input

第一行为一个整数s, 表图的个数.
接下来每一个二进制串, 第 i 行的二进制串为 gi, 其中 gi 是原图通过以下伪代码转化得
到的. 图的结点从 1 开始编号, 下面设结点数为 n.
Algorithm 1 Print a graph G = (V, E)
for i = 1 to n do
for j = i + 1 to n do
if G contains edge (i, j) then
print 1
else
print 0
end if
end for
end for
 2 ≤ n ≤ 10,1 ≤ s ≤ 60.

Output

输出一行一个整数, 表示方案数

Sample Input

3
1
1
0

Sample Output

4

HINT

设f[m]代表把图中的n个点放入m个集合中,任何2个集合绝对不联通,属于同一个集合内的点随意连边。
设g[m]代表把图中n个点分为m个联通块的方案数,那么答案就是g[1]。
f的求法:枚举子集划分,对于任意不属于同一集合的点x,y,我们令num这个二进制数代表图i中x,y是否直接相连,那么我们把每个num插入线性基中,贡献就是2^empty,empty代表线性基中空元素的个数。
其次,f[m]=Σg[i]*S2(i,m)  (m<=i<=n),其中s2代表第二类斯特林数。
通过斯特林反演可得g[m]=Σf[i]*S1(i,m)*(-1)^(i-1),其中s1代表第一类斯特林数(菜鸡是死背公式的)
我们只要求g[1],且S1(i,m)= i!.
#include
#include
#include
#include
#include
#include
using namespace std;
const int MAXN = 65;
int g[MAXN][MAXN], col[MAXN];
long long gay[MAXN], f[MAXN], s1[65][65], ans, a[MAXN];
int n, m, len, i, j, k, x, y, z, t;
char s[MAXN];
inline int get()
{
	char c;
	while ((c = getchar()) < 48 || c > 57);
	int res = c - 48;
	while ((c = getchar()) >= 48 && c <= 57)
		res = res * 10 + c - 48;
	return res;
}
inline void check(int all)
{
	for(i = 0; i < n; i ++)
		gay[i] = 0;
	int cnt = 0;
	for(int x = 1; x < m; x ++)
		for(int y = x + 1; y <= m; y ++)
			if (col[x] != col[y])
			{
				long long num = 0;
				for(int i = 1; i <= n; i ++)
					if (a[i] >> (g[x][y] - 1) & 1) num |= 1LL << i - 1;
				for(int i = n - 1; i >= 0; i --)
					if (num >> i & 1)
					{
						if (!gay[i]) {gay[i] = num; break;}
						else num ^= gay[i];
					}
			}
	for(int i = 0; i < n; i ++)
		if (gay[i]) cnt ++;
	f[all] += 1LL << n - cnt;
}
inline void dfs(int k, int all)
{
	if (k > m) {check(all); return;}
	for(int i = 1; i <= all; i ++)
		col[k] = i, dfs(k + 1, all), col[k] = 0;
	col[k] = all + 1;
	dfs(k + 1, all + 1);
	col[k] = 0;
}
int main()
{
	cin >> n;
	for(i = 1; i <= n; i ++)
	{
		scanf("%s", s + 1);
		len = strlen(s + 1);
		for(j = 1; j <= len; j ++)
			if (s[j] == '1') a[i] |= 1LL << j - 1;
	}
	m = 1;
	while (m * (m - 1) / 2 < len) m ++;
	i = 1; j = 2;
	for(k = 1; k <= len; k ++)
	{
		g[i][j] = k;
		j ++;
		if (j > m) i ++, j = i + 1;
	}
	dfs(1, 0);
	s1[0][0] = 1;
	for(i = 1; i <= m; i ++)
	{
		for(j = 1; j < i; j ++)
			s1[i][j] = s1[i - 1][j - 1] + s1[i - 1][j] * (i - 1);
		s1[i][i] = 1;
	}
	for(i = 1; i <= m; i ++)
		if (i & 1) ans += f[i] * s1[i][1];
		else ans -= f[i] * s1[i][1];
	cout << ans << endl;
}


你可能感兴趣的:(数论少许知识)