定义两个结点数相同的图 G1 与图 G2 的异或为一个新的图 G, 其中如果 (u, v) 在 G1 与
G2 中的出现次数之和为 1, 那么边 (u, v) 在 G 中, 否则这条边不在 G 中.
现在给定 s 个结点数相同的图 G1…s, 设 S = {G1, G2, … , Gs}, 请问 S 有多少个子集的异
或为一个连通图?
我们有一个想法,就是枚举子图的子集,然后再来搞,这样显然不行,因为m有60这么大
另外一个方向,我们从n开始入手,用贝尔数的时间(其实就是第二类斯特林数的一行的和)来进行把这些点划分,然后同一个集合里面的点可以有边也可以没有,不同集合一定没边
至于算方案数,我们可以把每一条不在集合的边变成一个二进制位,有边则为1,没有则为0,插入线性基,看看自由元个数,假设为cnt,那么答案就贡献
考虑这个方案数其实代表什么,代表的就是一些子图子集的划分,变成这样的一个至少为m联通块的方案数
考虑一种子图子集的划分,会被算到的次数是
#include
#define ll long long
#define bin(i) (1ll<<(i))
#define c(i,j) (fac[(i)] * inv[(j)] % mod * inv[(i) - (j)] % mod)
using namespace std;
const int N = 65;
inline int read()
{
char ch=getchar(); int p=0; int f=1;
while(ch<'0' || ch>'9'){if(ch=='-') f=-1; ch=getchar();}
while(ch>='0' && ch<='9'){p=p*10+ch-'0'; ch=getchar();}
return p*f;
}
int s; char ss[12*12]; int n;
bool v[N][12][12];
int bel[12]; int fac[12]; int cnt; ll b[N],ans = 0;
void dfs(ll x,ll y)
{
if(x>n)
{
cnt = 0;
for(int k=1;k<=s;k++)
{
int l=0; ll t=0;
for(int i=1;i<=n;i++) for(int j=i+1;j<=n;j++) if(bel[i] != bel[j]) t|=bin(l) * v[k][i][j],l++;
// printf("%lld\n",t);
for(int i=1;i<=cnt;i++) if((t^b[i]) < t) t^=b[i];
if(t) b[++cnt] = t;
}
ans = ans + bin(s-cnt) * ((y&1) ? 1 : (-1)) * fac[y-1];
// printf("%lld\n",bin(s-cnt) * ((y&1) ? 1 : (-1)) * fac[y-1]);
// printf("%lld %lld %lld\n",x,y,bin(s-cnt) * ((y&1) ? 1 : (-1)) * fac[y-1]);
return ;
}
for(int i=1;i<=y+1;i++) bel[x] = i,dfs(x+1,y+(i>y));
}
int main()
{
s = read();
for(int i=1;i<=s;i++)
{
scanf("%s",ss+1); int len = strlen(ss + 1);
for(int j=1;j<=10;j++) if(j*(j-1)/2 == len){n=j; break;}
int l=1; for(int j=1;j<=n;j++) for(int k=j+1;k<=n;k++,l++) if(ss[l]=='1') v[i][j][k] = v[i][k][j] = 1;
}
fac[0] = 1; for(int i=1;i<=n;i++) fac[i] = fac[i-1] * i ;
dfs(1,0);
return printf("%lld\n",ans),0;
}