您的位置:首页 > 其它

bzoj 4671: 异或图 容斥+斯特林反演+线性基

2018-04-03 09:20 309 查看

题意

定义两个结点数相同的图 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,那么答案就贡献2cnt
1a7a6
2cnt

考虑这个方案数其实代表什么,代表的就是一些子图子集的划分,变成这样的一个至少为m联通块的方案数

考虑一种子图子集的划分,会被算到的次数是

∑i=1mS(m,i)∑i=1mS(m,i)

之后另外一个现实,有一个容斥系数为(−1)m−1(m−1)!(−1)m−1(m−1)!

就可以算出联通块个数恰好为n=1的情况

下面简单证明一下:

∑m=1n(−1)m−1(m−1)!S(n,m)=∑m=1n−1(−1)m−1(m−1)!(S(n−1,m−1)+mS(n−1,m))+(−1)n−1(n−1)!=∑m=1n−1(−1)m−1(m−1)!S(n−1,m−1)+∑m=1n−1(−1)m−1m!S(n−1,m)+(−1)n−1(n−1)!=∑m=1n−1(−1)m−1(m−1)!S(n−1,m−1)+∑m=1n−2(−1)m−1m!S(n−1,m)=0[n>1](1)(1)∑m=1n(−1)m−1(m−1)!S(n,m)=∑m=1n−1(−1)m−1(m−1)!(S(n−1,m−1)+mS(n−1,m))+(−1)n−1(n−1)!=∑m=1n−1(−1)m−1(m−1)!S(n−1,m−1)+∑m=1n−1(−1)m−1m!S(n−1,m)+(−1)n−1(n−1)!=∑m=1n−1(−1)m−1(m−1)!S(n−1,m−1)+∑m=1n−2(−1)m−1m!S(n−1,m)=0[n>1]

显然只有当n=1时上式=1

然后就没了,这个东西叫做斯特林反演?

还要卡卡常

代码

#include <bits/stdc++.h>
#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
[12][12];

int bel[12]; int fac[12]; int cnt; ll b
,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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: