我们令a[i]表示看守的第i扇门对应囚犯的哪一扇门。令图G为有n个节点的图,编号为1~n。对于满足1≤i<j≤n的一对i和j,如果有a[i]>a[j],那么在G中编号为i和j的节点之间连一条边。得到的图G被称为逆序图。对于图G=(V,E),非空点集S∈V是一个独立集当且仅当对于任意两个点u,v∈V,不存在(u,v)∈E。而S是一个覆盖集当且仅当对于任意点v?S存在点u∈S满足(u,v)∈E。我们在意的是,图G中有多少个点集既是独立集又是覆盖集。出于某种不知名的原因,被迫参加监狱游戏的大家的安危和这个问题的答案有关。拜托了,请一定要求出这个方案数。
输入第一行含有两个整数n和m,表示逆序图的点数和边数。
接下来m行,每行描述一条边。每行包含两个1~n的整数,代表边的两个端点。保证没有重边和自环。
保证给定的图是一个合法的逆序图,即,存在至少一个序列,按照题目描述中所述方法得到的逆序图是给定的图。
n≤1000,0≤m≤(n(n-1))/2
输出一个整数,表示方案数对1,000,000,007取模得到的结果。
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int P=1000000007;
int n,m,ans;
int map[1010][1010],f[1010],nxt[1010],pre[1010],v[1010],bef[1010],aft[1010];
inline int rd()
{
int ret=0,f=1; char gc=getchar();
while(gc<‘0‘||gc>‘9‘) {if(gc==‘-‘) f=-f; gc=getchar();}
while(gc>=‘0‘&&gc<=‘9‘) ret=ret*10+gc-‘0‘,gc=getchar();
return ret*f;
}
int main()
{
n=rd(),m=rd();
int i,j,k,a,b;
for(i=1;i<=m;i++)
{
a=rd(),b=rd();
if(a>b) swap(a,b);
map[a][b]=1,nxt[a]++,pre[b]++;
}
for(i=1;i<=n;i++) for(j=i+1;j<=n;j++)
{
if(!map[i][j]) bef[j]=aft[i]=1;
}
for(i=1;i<=n;i++)
{
for(k=1;k<=n;k++) if(!v[k]&&!nxt[k]&&pre[k]==k-1) break;
v[k]=i;
for(j=1;j<k;j++) if(map[j][k]) nxt[j]--;
for(j=k+1;j<=n;j++) pre[j]++;
}
for(i=1;i<=n;i++)
{
f[i]=(f[i]+(!bef[i]))%P;
for(k=n+1,j=i+1;j<=n;j++) if(!map[i][j]&&v[j]<k) k=v[j],f[j]=(f[j]+f[i])%P;
if(!aft[i]) ans=(ans+f[i])%P;
}
printf("%d",ans);
return 0;
}