Description
现在有n个人要排成一列,编号为1->n 。但由于一些不明原因的关系,人与人之间可能存在一些矛盾关系,具体有m条矛盾关系(u,v),表示编号为u的人想要排在编号为v的人前面。要使得队伍和谐,最多不能违背k条矛盾关系(即不能有超过k条矛盾关系(u,v),满足最后v排在了u前面)。问有多少合法的排列。答案对10^9+7取模。
输入文件名为count.in。 第一行包括三个整数n,m,k。 接下来m行,每行两个整数u,v,描述一个矛盾关系(u,v)。 保证不存在两对矛盾关系(u,v),(x,y),使得u=x且v=y 。
Output
输出文件名为count.out。 输出包括一行表示合法的排列数。
输入1:
4 2 1 1 3 4 2
输入2:
10 12 3 2 6 6 10 1 7 4 1 6 1 2 4 7 6 1 4 10 4 10 9 5 9 8 10
Sample Output
输出1:
18
输出2:
123120
Data Constraint
对于30%的数据,n<=10 对于60%的数据,n<=15 对应100%的数据,n,k<=20,m<=n*(n-1),保证矛盾关系不重复。
Solution
看到数据范围,显然就是状压DP了。
设
F[s][i]
表示已经选了的人的集合为
s
、已经违背了 i 条矛盾关系 的 合法排列数。
转移时枚举将要选的人,处理出会产生的矛盾关系即可。
那么如何快速处理出将会产生的矛盾关系呢?
考虑预处理出
a[x]
表示排在
x
以前会有矛盾的人的集合,
那么与 s & 的值二进制的 1 的个数就是所求的数量了。
时间复杂度为
O(2N∗N∗K)
。
Code
using namespace std;
const
int mo=
1e9+
7;
int n,
m,k,ans;
int a[
21],g[
1<<
20],p[
21],f[
1<<
20][
21];
inline
int read()
{
int X=
0,w=
1; char ch=
0;
while(ch<
'0' || ch>
'9') {
if(ch==
'-') w=-
1;ch=getchar();}
while(ch>=
'0' && ch<=
'9') X=(X<<
3)+(X<<
1)+ch-
'0',ch=getchar();
return X
*w;
}
inline void dfs(
int x,
int y,
int z)
{
if(z>n)
return;
g[
x]=
y;
dfs(
x,
y,z+
1);
dfs(
x+p[z],
y+
1,z+
1);
}
int main()
{
n=
read(),
m=
read(),k=
read();
for(
int i=p[
0]=
1;i<=n;i++) p[i]=p[i-
1]<<
1;
dfs(
0,
0,
0);
for(
int i=
1;i<=
m;i++)
{
int x=
read(),
y=
read();
a[
y]|=p[
x-
1];
}
f[
0][
0]=
1;
for(
int s=
0;
s<p[n];
s++)
for(
int j=
1;j<=n;j++)
if(p[j-
1]&
s)
{
int sum=g[
s&a[j]];
for(
int i=sum;i<=k;i++) f[
s][i]=(f[
s][i]+f[
s-p[j-
1]][i-sum])
%mo;
}
for(
int i=
0;i<=k;i++) ans=(ans+f[p[n]-
1][i])
%mo;
printf(
"%d",ans);
return 0;
}