题目描述
在一个大小为$N\times N$的棋盘上,放置了$N$个黑色的棋子。并且,对于棋盘的每一行和每一列,有且只有一个棋子。
现在,你的任务是再往棋盘上放置$N$个白色的棋子。显然,白色棋子不能与黑色棋子重合。在此基础上,放置的方式还需要满足:对于棋盘的每一行和每一列,有且只有一个白色棋子。
当然,放置的方式有很多种,你只需要输出不同的放置方案数即可。
输入格式
输入文件为$board.in$。
第一行包含一个正整数$N$。
接下来$N$行,每行$N$个整数用于描述棋盘。$0$表示这个位置是空的,而$1$表示这个位置有一个黑棋子。
输出格式
输出文件为$board.out$。
一行一个整数,表示合法的放置方案数。
样例
样例输入:
2
0 1
1 0
样例输出:
1
数据范围与提示
对于$20\%$的数据,满足$N\leqslant 10$。
对于$60\%$的数据,满足$N\leqslant 20$。
对于$100\%$的数据,满足$1\leqslant N\leqslant 200$。
题解
对于每一列,哪一行放黑子无所谓,那个就是逗你开心的。
先说一下我在考场上的思路,我想到了容斥和组合数。
首先,如果不考虑黑子,那么我们有$N!$种放法。
然后我们可以考虑有黑子的情况,设$f[i]$表示至少有$i$列合法的方案数,此处的合法是指白子和黑子没有放到一起,则:
$$f[i]=(N-i)!\times C_N^i$$
先来解释式子,$(N-i)!$表示剩下的我们还是可以随便选,$C_N^i$则表示在$N$列中选出$i$列。
最后答案就是$\sum \limits_{i=1}^N (-1)^i f[i]$。
然而这个式子太麻烦了,于是$170$行各种高精多和一就爆炸了……
下面来讲官方题解,问题就是让求$N$个数的错排方案数,那么直接用递推式即可:
$$ans_n=(ans_{n-1}+ans_{n-2})\times (n-1)$$
时间复杂度:$\Theta(n^2)$。
期望得分:$100$分。
实际得分:$100$分。
代码时刻
#include<bits/stdc++.h> using namespace std; int n; long long f[201][1000],flag1,flag2; void pls(int x) { int jw=0; f[x][0]=f[x-1][0]; for(int i=1;i<=f[x][0];i++) { f[x][i]=f[x-1][i]+f[x-2][i]+jw; jw=f[x][i]/10; f[x][i]%=10; } for(int i=f[x][0]+1;i;i++) { if(!jw)break; f[x][i]+=jw; jw=f[x][i]/10; f[x][i]%=10; f[x][0]=i; } } void che(int x) { flag2=0; for(int i=1;i<=f[x][0];i++) { flag1=f[x][i]*(x-1)+flag2; f[x][i]=flag1%10; flag2=flag1/10; } if(flag2)f[x][++f[x][0]]=flag2; } int main() { scanf("%d",&n); f[1][1]=0; f[2][1]=1; f[2][0]=1; for(int i=3;i<=n;i++) { pls(i); che(i); } cout<<f[n][f[n][0]]; while(--f[n][0]) { cout.fill(‘0‘); cout<<setw(1)<<f[n][f[n][0]]; } return 0; }
rp++
原文地址:https://www.cnblogs.com/wzc521/p/11649721.html