异或图[bzoj4671]

题目链接

darkbzoj

题意

定义两个结点数相同的图 G1 与图 G2 的异或为一个新的图 G,其中如果 (u,v)G1G2 中的出现次数之和为 1,那么边 (u,v)G 中,否则这条边不在 G 中。

现在给定 s 个结点数相同的图 G1s, 设 S=G1,G2,,Gs ,请问 S 有多少个子集的异或为一个连通图?

n10,s60

题解

fi 表示恰好有 i 个连通块的方案数。

这个显然比较难求,那么再设一个 gi 表示至少有 i 个连通块的方案数。

考虑用 fj 表示 gi,系数就是将 j 个不同的连通块放到 i 个相同的盒子里方案数,即第二类斯特林数 S2(j,i)

那么有:gi=j=inS2(j,i)fj

斯特林反演一下得到:fi=j=inS1(j,i)(1)jigj

我们需要求的是 f1=i=1n(i1)!(1)i1gi

因此题目可转换成求 gi

由于点数比较少,我们可以枚举所有的集合划分,每个集合内的点可以随便连,不同集合之间的点不存在连边。

然后我们只考虑不同集合之间的边,对于每个图,是否存在这些边可以写成一个二进制数,需要在 s 个数中选一些,使得异或起来为 0,这个用线性基统计一下即可。

程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <vector>
#include <cstring>
#include <queue>
using namespace std;
#define FO(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout)
#define fo(i,j,k) for(int i=(j),end_i=(k);i<=end_i;i++)
#define ff(i,j,k) for(int i=(j),end_i=(k);i< end_i;i++)
#define fd(i,j,k) for(int i=(j),end_i=(k);i>=end_i;i--)
#define DEBUG(x) cerr<<#x<<"="<<x<<endl
#define all(x) (x).begin(),(x).end()
#define cle(x) memset(x,0,sizeof(x))
#define lowbit(x) ((x)&-(x))
#define ll long long
#define ull unsigned ll
#define db double
#define lb long db
#define pb push_back
#define mp make_pair
#define fi first
#define se second
inline int read()
{
int x=0; char ch=getchar(); bool f=0;
for(;ch<'0'||ch>'9';ch=getchar()) if(ch=='-') f=1;
for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<3)+(x<<1)+(ch^48);
return f?-x:x;
}
int n,m,a[12];
bool g[62][12][12];
ll ans,fac[12],base[64];
inline void calc(int siz)
{
memset(base,0,sizeof(base));
int cnt,tot=0; ll sum;
ff(k,0,m)
{
cnt=sum=0;
ff(i,0,n)
ff(j,i+1,n)
if(a[i]!=a[j])
sum|=((ll)g[k][i][j]<<cnt),cnt++;
fd(i,cnt-1,0)
if((sum>>i)&1)
{
if(!base[i]) {tot++,base[i]=sum; break;}
else sum^=base[i];
}
}
if(siz&1) ans+=(1ll<<(m-tot))*fac[siz-1];
else ans-=(1ll<<(m-tot))*fac[siz-1];
}
void dfs(int u,int k)
{
if(u>=n) return calc(k);
fo(i,1,k+1) a[u]=i,dfs(u+1,max(i,k));
}
char s[100];
int main()
{
m=read();
ff(k,0,m)
{
scanf("%s",s); int t=0;
if(!n) n=(1+sqrt(1+8*strlen(s)))/2;
ff(i,0,n)
ff(j,i+1,n)
g[k][i][j]=(s[t++]-'0');
}
fac[0]=1;
fo(i,1,n) fac[i]=fac[i-1]*i;
dfs(0,0);
printf("%lld",ans);
return 0;
}