题解:AT_agc016_f [AGC016F] Games on DAG
cike_bilibili · · 题解
首先题目等价于问有多少种图满足
考虑每一次加入一种值,那么这种值显然要和每一种小于它的值的至少一个点连边,并且同一种值之间不能连边。
考虑设状态
有一个枚举子集的操作,容易做到
#include <bits/stdc++.h>
#define ll long long
#define ull unsigned long long
using namespace std;
namespace Manaka_Ao{
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<20,stdin),p1==p2)?EOF:*p1++)
char buf[1<<20],*p1=buf,*p2=buf;
inline int read(){
int ans=0,w=1;
char ch=getchar();
while(ch<48||ch>57){
if(ch=='-')w=-1;
ch=getchar();
}
while(ch>=48&&ch<=57){
ans=(ans<<1)+(ans<<3)+ch-48;
ch=getchar();
}
return w*ans;
}
const int mod=1000000007;
const int N=20;
int n,m;
struct edge{
int x,y;
}ed[N*N];
int to[N][N];
int f[(1<<15)+5];
int c[N][(1<<15)+5];
signed main(){
n=read(),m=read();
int U=(1<<n)-1;
for(int i=1;i<=m;i++){
int x=read(),y=read();
to[x][y]=1;
}
for(int i=1;i<=n;i++){
for(int j=0;j<(1<<n);j++){
for(int k=1;k<=n;k++)if(j&(1<<k-1))c[i][j]+=to[i][k];
}
}
f[0]=1;
for(int i=1;i<(1<<n);i++){
if((i&1)^(i>>1&1))continue;
for(int j=i;j;j=(j-1)&i){
if((j&1)^(j>>1&1))continue;
ll ans=1;
for(int k=1;k<=n;k++)if((U^i)&(1<<k-1))ans=ans*((1<<c[k][j])-1)%mod;
for(int k=1;k<=n;k++)if(j&(1<<k-1))ans=ans*(1<<c[k][U^i])%mod;
f[i]=(f[i]+f[i^j]*ans)%mod;
}
}
ll ans=1;
for(int i=1;i<=m;i++)ans=ans*2%mod;
cout<<(ans-f[U]+mod)%mod<<'\n';
return 0;
}
}
signed main(){
Manaka_Ao::main();
return 0;
}