题目大意:
题目链接:https://jzoj.net/senior/#main/show/3059
求
含
个障碍网格中选择
个格子,使得任意两个格子不在同一行、同一列的方案数。
思路:
由于
只有
,可以考虑用总方案数
放在障碍上的方案数。
总方案数很明显是
因为第一行有
个格子可以选,选择其中一个后,第二行就有
个格子可选,第三行就有
个可选,一直到最后一行只有
个可选。总方案数就是
然后我们会发现总方案数里面包含了选择
个障碍的方案数。所以我们再减去选择
个的方案数。此时我们会发现,总方案数里面包含了选择
个方案数,减去选择一个的方案数后,减去的也包含了选择
个障碍的方案数。相抵消,就等于没有减去选择
个障碍的方案数。于是我们就要减去选择
个的方案数。然后加上选择
个,减去选择
个。。。
用人话说,设
表示在
网格中选择
个格子,其中有
个格子是障碍的情况数量。那么答案就是
那么如何求
很明显,
选择
个障碍的方案数
选择
个非障碍的方案数。
选择
个障碍的方案数可以用搜索求。因为
只有
,可以
判断是否选择则个障碍。
那么选择
个非障碍的方案数其实就是要我们求在
的格子里选择
个格子的方案数,自然就是
了。
的阶乘要提前预处理,时间复杂度
。
代码:
#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;
}