【描述】
给定一个有向图,求拓扑排序生成的序列数
【分析】
我们知道当所有儿子节点排好序的时候,父节点就排好序了。
这里我们定义一种状态:状态s的二进制位上的1表示此点已经排好序了。
例如:s=6时,化为二进制s=110,表示第2、3个点已经排好序了。
所以父节点的状态可以由子节点转移而来。
用son[i]表示节点i可以进行转移的合法状态,f[s]表示状态为s的方法数。
然后枚举所有的状态,然后在此状态中找二进制位上是0的点,如果这个点要求的合法状态是当前状态的子状态,那么可以由当前状态转移到把第i位设为1的状态。
详见代码:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
#include<cmath>
#include<algorithm>
using namespace std;
typedef long long ll;
#define FILE "read"
#define up(i,j,n) for(ll i=j;i<=n;i++)
namespace INIT{
char buf[1<<15],*fs,*ft;
inline char getc() {return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;}
inline ll read() {
ll x=0,f=1; char ch=getc();
while(!isdigit(ch)) {if(ch=='-') f=-1; ch=getc();}
while(isdigit(ch)) {x=x*10+ch-'0'; ch=getc();}
return x*f;
}
}using namespace INIT;
ll n,m,son[30],f[1<<17];
int main(){
freopen(FILE".in","r",stdin);
freopen(FILE".out","w",stdout);
n=read(); m=read();
up(i,1,m) {ll x=read(),y=read(); son[x]|=(1<<(y-1));}
f[0]=1;
up(s,0,(1<<n)-1) if(f[s]>0) up(i,1,n) if((s&son[i])==son[i]&&(s&(1<<(i-1)))==0) f[s|(1<<(i-1))]+=f[s];
printf("%lld\n",f[(1<<n)-1]);
return 0;
}