cls在wc讲的神题,无限Orz
直接求原图强联通的生成子图个数不好求,考虑用总方案数减不是强联通的方案数
如果原图不是强联通,缩点后一定是一个点数>1的DAG
有一个比较暴力的做法,即枚举每个强联通分量,将它缩点
令
f[S]表示枚举了S内的强联通分量DAG的个数,cnt[S]表示S内的边数
由于一个DAG一定有出度为0的点为汇,我们可以枚举汇集
T
,
令
f[S]=∑T⊆Sf[S−T]2ways(S−T,T)
但是这样做是不对的…
比如汇集是点A,B的情况,我们枚举A时会算到这种情况,枚举B时也会算到这种情况…..
考虑容斥,因为对于集合S,有
∑T⊆S,T≠ϕ(−1)|T|−1=1
(集合容斥,和普通的容斥其实是一个东西)
于是我们就得到了正确的dp式
f[S]=∑T⊆S(−1)|T|−1f(S−T)2ways(S−T,T)
但是这样做复杂度太高了因为要枚举所有可能的强联通情况
事实上对于同一个点集
T
包含的点,枚举强联通只是为了统计
而这个东西我们可以直接dp统计
令
g[T]表示T的所有强联通划分中,奇数个强联通的方案数减偶数个强联通的方案数
f[S]表示S的强联通的生成子图个数
转移g时固定一个点u,枚举包含u的强联通块
转移f时,枚举出度为0的强联通汇集包含的点集T
(这里当会发现f[S]和g[S]会互相调用,但事实上f[S]的dp式中当T=S时,g[T]不包含f[S]的情况因为若包含了计算的就不是DAG而直接是强联通了)
然后就可以 O(3n) 啦
code:
#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
#define lowbit(x) x&(-x)
using namespace std;
const int maxn = 15;
const int mask2 = 1<<15;
const int mask3 = 14348907;
const int mod = 1e9+7;
inline void add(int &a,const int &b){a+=b;if(a>=mod)a-=mod;if(a<0)a+=mod;}
int pw2[maxn*maxn],pw3[maxn];
int to3[mask2];
int sum[mask2],bac[mask2];
void pre()
{
pw2[0]=1,pw3[0]=1;
for(int i=1;i<maxn*maxn;i++) pw2[i]=(pw2[i-1]<<1)%mod;
for(int i=1;i<maxn;i++) pw3[i]=pw3[i-1]*3;
for(int i=1;i<mask2;i++) sum[i]=sum[i^lowbit(i)]+1;
for(int i=0;i<maxn;i++) bac[1<<i]=i+1;
to3[0]=0;
for(int i=1;i<mask2;i++) to3[i]=to3[i^lowbit(i)]+pw3[bac[lowbit(i)]-1];
}
int n,m,al;
int outd[maxn],ind[maxn],cnt[mask2];
int ways[mask3];
void dpway()
{
for(int i=1;i<al;i++)
{
int lb=lowbit(i),x=bac[lb];
cnt[i]=cnt[i^lb]+sum[outd[x]&i]+sum[ind[x]&i];
}
for(int i=1;i<al;i++) for(int j=(i-1)&i;j;j=(j-1)&i)
{
int u=i^j,v=j,lb=lowbit(v),x=bac[lb];
ways[to3[u]+to3[v]*2]=ways[to3[u]+to3[v^lb]*2]+sum[ind[x]&u];
}
}
// connected Divide
int f[mask2],g[mask2];
void dp()
{
g[0]=-1;
for(int i=1;i<al;i++)
{
int x=bac[lowbit(i)];
for(int j=(i-1)&i;j;j=(j-1)&i) if(j>>x-1&1)
add(g[i],-(ll)f[j]*g[i^j]%mod);
f[i]=pw2[cnt[i]];
for(int j=i;j;j=(j-1)&i)
add(f[i],-(ll)g[j]*pw2[cnt[i^j]+ways[to3[i^j]+to3[j]*2]]%mod);
add(g[i],f[i]);
}
}
int main()
{
pre();
scanf("%d%d",&n,&m); al=1<<n;
for(int i=1;i<=m;i++)
{
int x,y; scanf("%d%d",&x,&y);
outd[x]|=1<<y-1;
ind[y]|=1<<x-1;
}
dpway();
dp();
int ans=f[al-1]; add(ans,0);
printf("%d\n",ans);
return 0;
}