bzoj4671 异或图
原题地址:http://www.lydsy.com/JudgeOnline/problem.php?id=4671
题意:
定义两个结点数相同的图 G1 与图 G2 的异或为一个新的图 G, 其中如果 (u, v) 在 G1 与G2 中的出现次数之和为 1, 那么边 (u, v) 在 G 中, 否则这条边不在 G 中。
现在给定 s 个结点数相同的图 G1…s, 设 S = {G1, G2, … , Gs}, 请问 S 有多少个子集的异
或为一个连通图?
数据范围
2 ≤ n ≤ 10,1 ≤ s ≤ 60
题解:
好题。
n的范围是10,那么边的范围为50,显然不能直接枚举边的子集再看是否联通。
而考虑到10的贝尔数是一个可枚举的范围,可以枚举点的划分计算方案然后容斥。
即,不同集合内的点没有边,相同集合内的点任意连的方案数,
对于这个方案数,我们只需要跑个高斯消元,解的数量就是
2(s−|B|)
2
(
s
−
|
B
|
)
。
(关于
2(s−|B|)
2
(
s
−
|
B
|
)
我的理解是和线性基相同,最后消出来得到的
s−|B|
s
−
|
B
|
个自由元,对于每个数(在此处二进制位上表示的是每个图的存在情况),表示的方法是唯一的。后面被消为0的数一定可以保留的主元表示出来,
2(s−|B|)
2
(
s
−
|
B
|
)
即异或为0的集合数量,与原来那一个解异或得到的值相同,
2(s−|B|)
2
(
s
−
|
B
|
)
就是解的数量)
UPD:我是在误人子弟了…实际上是完全不一样的。对于此题,可以说是最后剩多少个主元,就有多少个图的存在性是确定的,那么剩下的随意有无就是
2(s−|B|)
2
(
s
−
|
B
|
)
。
对于容斥系数我们得到:
∑i=1m{m i}f(i)=[m==1]
∑
i
=
1
m
{
i
m
}
f
(
i
)
=
[
m
==
1
]
f(i)
f
(
i
)
可以
m2
m
2
递推。(或者打个表发现
f(i)=(−1)i−1(i−1)!
f
(
i
)
=
(
−
1
)
i
−
1
(
i
−
1
)
!
,代回去可以证出来)
于是用贝尔数的时间枚举子集划分,然后跑高消,乘上对应的容斥系数计入答案即可,
O(B(n)n2s)
O
(
B
(
n
)
n
2
s
)
。
( 注意:此代码的高消部分存在问题,暂未改正,请勿参考)
代码:
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#define LL long long
using namespace std;
const int N=15;
const int SS=61;
char str[105];
int g[SS][N][N],s,n,S[N][N],f[N],col[N];
LL pw[SS],ans=0,C[SS];
void dfs(int x,int sz)
{
if(x==n+1)
{
int tot=0;
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++)
{
LL cur=0;
if(col[i]!=col[j])
{
for(int ss=1;ss<=s;ss++)
if(g[ss][i][j]) cur+=pw[ss-1];
for(int k=1;k<=tot;k++)
if((cur^C[k])<cur) cur=cur^C[k];
if(cur) C[++tot]=cur;
}
}
ans+=1LL*f[sz]*pw[s-tot];
return;
}
for(int i=1;i<=sz;i++) {col[x]=i; dfs(x+1,sz);}
col[x]=sz+1; dfs(x+1,sz+1);
}
int main()
{
scanf("%d",&s);
for(int ss=1;ss<=s;ss++)
{
scanf("%s",str); int len=strlen(str);
for(n=1;;n++) if(n*(n-1)==2*len) break;
for(int l=0,i=1;i<=n;i++)
for(int j=i+1;j<=n;j++)
g[ss][i][j]=g[ss][j][i]=str[l++]-'0';
}
for(int i=0;i<=s;i++) pw[i]=1LL<<i;
for(int i=0;i<=n;i++) for(int j=0;j<=i;j++)
if(i==j) S[i][j]=1; else S[i][j]=S[i-1][j-1]+j*S[i-1][j];
f[1]=1;
for(int i=2;i<=n;i++)
for(int j=1;j<i;j++) f[i]-=f[j]*S[i][j];
dfs(1,0);
printf("%lld\n",ans);
return 0;
}