题目大意:
题目链接:https://jzoj.net/senior/#main/show/3059
求
n
×
n
n\times n
n×n含
m
m
m个障碍网格中选择
n
n
n个格子,使得任意两个格子不在同一行、同一列的方案数。
思路:
由于
m
m
m只有
10
10
10,可以考虑用总方案数
−
-
−放在障碍上的方案数。
总方案数很明显是
n
!
n!
n!
因为第一行有
n
n
n个格子可以选,选择其中一个后,第二行就有
n
−
1
n-1
n−1个格子可选,第三行就有
n
−
2
n-2
n−2个可选,一直到最后一行只有
1
1
1个可选。总方案数就是
n
×
(
n
−
1
)
×
.
.
.
×
1
=
n
!
n\times (n-1)\times ...\times 1=n!
n×(n−1)×...×1=n!
然后我们会发现总方案数里面包含了选择
1
1
1个障碍的方案数。所以我们再减去选择
1
1
1个的方案数。此时我们会发现,总方案数里面包含了选择
2
2
2个方案数,减去选择一个的方案数后,减去的也包含了选择
2
2
2个障碍的方案数。相抵消,就等于没有减去选择
2
2
2个障碍的方案数。于是我们就要减去选择
2
2
2个的方案数。然后加上选择
3
3
3个,减去选择
4
4
4个。。。
用人话说,设
s
i
s_i
si表示在
n
×
n
n\times n
n×n网格中选择
n
n
n个格子,其中有
i
i
i个格子是障碍的情况数量。那么答案就是
{
n
!
−
s
1
+
s
2
−
s
3
+
s
4
−
.
.
.
−
s
n
−
1
+
s
n
(
n
%
2
=
=
0
)
n
!
−
s
1
+
s
2
−
s
3
+
s
4
−
.
.
.
+
s
n
−
1
−
s
n
(
n
%
2
=
=
1
)
\left\{\begin{matrix}n!-s_1+s_2-s_3+s_4-...-s_{n-1}+s_n(n\%2==0) \\n!-s_1+s_2-s_3+s_4-...+s_{n-1}-s_n(n\%2==1)\end{matrix}\right.
{n!−s1+s2−s3+s4−...−sn−1+sn(n%2==0)n!−s1+s2−s3+s4−...+sn−1−sn(n%2==1)
那么如何求
s
i
?
s_i?
si?
很明显,
s
i
=
s_i=
si=选择
i
i
i个障碍的方案数
×
\times
×选择
m
−
i
m-i
m−i个非障碍的方案数。
选择
i
i
i个障碍的方案数可以用搜索求。因为
m
m
m只有
10
10
10,可以
2
m
2^m
2m判断是否选择则个障碍。
那么选择
m
−
i
m-i
m−i个非障碍的方案数其实就是要我们求在
(
m
−
i
)
×
(
m
−
i
)
(m-i)\times(m-i)
(m−i)×(m−i)的格子里选择
m
m
m个格子的方案数,自然就是
m
!
m!
m!了。
0
∼
20
0\sim 20
0∼20的阶乘要提前预处理,时间复杂度
O
(
m
×
2
m
)
O(m\times 2^m)
O(m×2m)。
代码:
#include <cstdio>
#include <iostream>
using namespace std;
typedef long long ll;
int n,m,x[15],y[15];
ll ans,num[25],s;
bool used[3][25];
void dfs(int i,int cnt,int sum)
//i表示处理到第i个障碍,cnt表示选择了多少个障碍,sum表示需要选择的总障碍数
{
if (cnt==sum) //选择完毕
{
s++; //方案数
return;
}
if (i>m) return;
if (!used[1][x[i]]&&!used[2][y[i]]) //这一行和这一列都没有被选择
{
used[1][x[i]]=used[2][y[i]]=1;
dfs(i+1,cnt+1,sum);
used[1][x[i]]=used[2][y[i]]=0;
}
dfs(i+1,cnt,sum);
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=m;i++)
scanf("%d%d",&x[i],&y[i]);
num[0]=1;
for (int i=1;i<=n;i++) //预处理阶乘
num[i]=i*num[i-1];
ans=num[n];
for (int i=1;i<=m;i++)
{
s=0;
dfs(1,0,i);
if (!s) break;
if (i&1) ans-=s*num[n-i];
else ans+=s*num[n-i];
}
printf("%lld\n",ans);
return 0;
}