迫真例题
设\(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)\)
#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\n",ans);
fclose(stdin);
fclose(stdout);
return 0;
}
原文:https://www.cnblogs.com/gmh77/p/13299741.html