题目描述
题解
迫真例题
设(g[i])表示至少i个连通块的方案,(f[i])表示恰好i个连通块的方案(注意“至少”的含义)
则有(g[i]=sum_{j>=i}S(j,i)f[i])
斯特林反演:https://www.cnblogs.com/jz-597/p/13210825.html
类似子集反演,有(f[i]=sum_{j>=i}(-1)^{j-i}s(j,i)g[i])
考虑求g,枚举集合划分,不同集合里的点之间不能有边
把有边的图丢到线性基S里,答案是(2^{s-|S|}-1)
因为线性基里面的子集都不能选,全集挖掉线性基后的任意非空子集都可以选,选完后通过线性基里面的元素可以唯一使其合法
注意线性基只用考虑两端在不同集合的边
时间复杂度大概是(O(B_n*s*n^2))
code
#include <bits/stdc++.h>
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
#define ll long long
//#define file
using namespace std;
int a[11][11],d[11],N,n,m,i,j,k,l;
ll p[61],f[61],b[61],g[11],ans;
char st[51];
bool Bz[51];
void add(ll t)
{
int i,j,k,l;
fd(i,m,1)
if (t&p[i-1] && Bz[i])
{
if (!b[i]) {b[i]=t,--ans;break;}
t^=b[i];
}
}
void dg(int t,int tot)
{
int i,j,k,l;
bool bz;
if (t>n)
{
memset(b,0,sizeof(b)),ans=N;
fo(i,1,n-1) fo(j,i+1,n) Bz[a[i][j]]=d[i]!=d[j];
fo(i,1,N)
{
bz=0;
fo(j,1,n-1)
{
fo(k,j+1,n)
if (d[j]!=d[k] && (f[i]&p[a[j][k]-1]))
{add(f[i]);bz=1;break;}
if (bz) break;
}
}
if (tot==2)
n=n;
g[tot]+=p[ans]-1;
return;
}
fo(i,1,tot) d[t]=i,dg(t+1,tot);
d[t]=tot+1,dg(t+1,tot+1);
}
int main()
{
#ifdef file
freopen("bzoj4671.in","r",stdin);
#endif
p[0]=1;
fo(i,1,60) p[i]=p[i-1]*2;
scanf("%d",&N);
fo(k,1,N)
{
scanf("%s",st+1);l=strlen(st+1);
fo(i,1,l) f[k]+=p[i-1]*(st[i]=='1');
if (!n)
{
while (n*(n-1)/2!=l) ++n;
l=0;
fo(i,1,n-1) fo(j,i+1,n) a[i][j]=++l;
}
}
m=n*(n-1)/2;
dg(1,0);
ans=0;
k=1;
fo(j,1,n)
{
if (j>1) k*=j-1;
ans+=(((j-1)&1)?-1:1)*g[j]*k;
}
printf("%lld
",ans);
fclose(stdin);
fclose(stdout);
return 0;
}