正着做不好做,于是我们考虑反着来,如何计算一个点集s的答案呢,一定是所有的方案减去不合法的方案,不合法的方案一定是缩完点后是一个DAG,那么就一定有度数为0的scc,于是我们枚举s的子集,就是说这些点构成的scc的度数为0,这里我们就需要容斥了,容斥的目的是算出s集组成不合法的DAG的方案数,因为我们没有办法确定这里有几个scc。于是我们提前处理出g[s]表示这里面的每种不同scc的方案的贡献是$-1^{num-1}$,然后它们和其余的点之间随便连边,其余的点之间也随便连边,然后g数组我们是枚举任意一个点,然后枚举它所在的scc,然后在通过f数组转移,f就是总方案减去所有子集度数为0时的方案。
妙妙啊。
1 #include <cstdio> 2 #define N 16 3 #define mod 1000000007 4 using namespace std; 5 int n,m,to[1<<N],cnt[1<<N],bin[N*N],e[1<<N]; 6 int f[1<<N],g[1<<N]; 7 int calc(int S,int T){ 8 int ans=0; 9 for(;S;S-=S&-S) 10 ans+=cnt[to[S&-S]&T]; 11 return ans; 12 } 13 int main(){ 14 scanf("%d%d",&n,&m); 15 bin[0]=1; 16 for(int i=1;i<=m;i++) 17 bin[i]=bin[i-1]*2%mod; 18 for(int i=1,u,v;i<=m;i++){ 19 scanf("%d%d",&u,&v); 20 to[1<<u-1]|=1<<v-1; 21 } 22 for(int i=1;i<bin[n];i++)cnt[i]=cnt[i>>1]+(i&1); 23 for(int i=1;i<bin[n];i++)e[i]=calc(i,i); 24 for(int i=1;i<bin[n];i++){ 25 int k=i&-i,s=i^k; 26 for(int j=(s-1)&s;j;j=(j-1)&s) 27 g[i]=(g[i]-1ll*g[i^j^k]*f[j|k]%mod+mod)%mod; 28 if(i^k)g[i]=(g[i]-g[i^k]+mod)%mod; 29 f[i]=bin[e[i]]; 30 for(int j=i;j;j=(j-1)&i) 31 f[i]=(f[i]-1ll*g[j]*bin[e[i^j]+calc(j,i^j)]%mod+mod)%mod; 32 (g[i]+=f[i])%=mod; 33 } 34 printf("%d ",f[bin[n]-1]); 35 return 0; 36 }