题意
有一个有向图。这张图有n个点和m条有向边。
他很好奇不相交的环(简单环)来覆盖所有点的方案数(数字可能很大请模998,244,353)。
1<=n<=20,1<=m<=n*(n-1)
分析
我好菜呀,比赛的时候一堆人都切了然而我想了一晚上都还是不会。。。
其实,不相交的环覆盖数量就相当于把原图变成二分图后完美匹配的方案数。
具体来说就是对于原图中一条i->j的边,变成i->j’。
然后就可以随便状压了。
代码
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=25;
const int MOD=998244353;
int n,m,bin[N],last[N],cnt,s[1048580],f[1048580];
struct edge{int to,next;}e[N*N];
void addedge(int u,int v)
{
e[++cnt].to=v;e[cnt].next=last[u];last[u]=cnt;
}
void updata(int &x,int y)
{
x+=y;x-=x>=MOD?MOD:0;
}
int main()
{
scanf("%d%d",&n,&m);
bin[0]=1;
for (int i=1;i<=n;i++) bin[i]=bin[i-1]*2;
for (int i=1;i<=m;i++)
{
int x,y;
scanf("%d%d",&x,&y);
addedge(x,y);
}
for (int i=1;i<bin[n];i++) s[i]=s[i>>1]+(i&1);
f[0]=1;
for (int i=0;i<bin[n]-1;i++)
{
if (!f[i]) continue;
int x=s[i]+1;
for (int j=last[x];j;j=e[j].next)
{
int to=e[j].to;
if (!(i&bin[to-1])) updata(f[i^bin[to-1]],f[i]);
}
}
printf("%d",f[bin[n]-1]);
return 0;
}