Description
Solution
f[i]表示状态i所代表的点构成的强连通图方案数。
g[i]表示状态i所代表的的点形成奇数个强连通图的方案数-偶数个强连通图的方案数。
g是用来容斥的。
先用f更新g。枚举状态i的编号最小点k所在连通块大小i-j,$g[i]=-\sum _{j\subset i}f[i-j]*g[j]$(此处g中不更新强连通图个数为1的。
设点集i中有sum条边,则:
$f[i]=2^{sum}-\sum _{j\subset i}2^{sum-w[j]}*g[j]$。其中w[j]是i射向j的边数,这些边被钦定不能选。
最后记得用f[i]更新g[i]。
Code
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
const int mod=1e9+;
int n,m,x,y;
int in[],out[];
int num[],sum[];
ll f[],g[],bin[],w[];
void calw(int s,int c)
{
if (!c) return;
calw(s,(c-)&s);
w[c]=w[c^(c&-c)]+num[in[c&-c]&s];
}
int main()
{
scanf("%d%d",&n,&m);
bin[]=;for (int i=;i<=m;i++) bin[i]=(bin[i-]<<)%mod;
for (int i=;i<=m;i++)
{
scanf("%d%d",&x,&y);x--;y--;
in[bin[y]]|=bin[x];
out[bin[x]]|=bin[y];
}
for (int i=;i<bin[n];i++) for (int j=;j<n;j++)
if (i&bin[j]) num[i]++; for (int i=;i<bin[n];i++)
{
int lowbit=i&-i,s=i^lowbit;
for (int j=s;j;j=s&(j-)) g[i]=(g[i]-f[j^i]*g[j]%mod)%mod; sum[i]=sum[s]+num[in[lowbit]&s]+num[out[lowbit]&s];
f[i]=bin[sum[i]];
calw(i,i); for (int j=i;j;j=i&(j-))
{
f[i]=(f[i]-bin[sum[i]-w[j]]*g[j]%mod+mod)%mod;
}
g[i]+=f[i];if (g[i]>=mod) g[i]%=mod;
}
cout<<f[bin[n]-]; }