【JZOJ5164】【NOIP2017模拟6.25】小A做CF
题目大意:
在一个 \(n \times n\) 的棋盘上,有 \(n\) 个障碍物,它们两两不同行不同列,如果再放 \(n\) 个棋子在棋盘上,条件也是两两不同行不同列且不能放在障碍物上,求一共有多少种方案。
正文:
首先来举个例子:
0 1 0
1 0 0
0 0 1
因为它们两两不同列,也就是说每列都有一个,那么干脆直接用一个数列 \(a\) 代替它的位置。\(a_i\) 表示第 \(i\) 列的障碍物的行数。那么上面的例子可以这么表示 \(a=\{2,1,3\}\)。
剩下的任务就是求方案数。如果第 \(i\) 列的棋子不能放在 \(a_i\) 上且放的行数不与其它棋子的行数相同,我们就可以发现,这是一个错位排列。所以跟这个矩阵没什么关系,关键点是在 \(n\) 上,可以不读入矩阵。
直接求错位排列,\(\texttt{60}\) 分,因为 \(n \leq 200\),所以正解要高精度运算。
代码:
void opt(int b[], int c[], int d, int e)
{
memset(a, 0, sizeof(a));
int g, v;
g = v = 0;
for (int i = 1; i <= max(b[0], c[0]) + 10; i++)
{
g = b[i] + c[i] + v;
v = g / 10;
a[i] = g % 10;
}
for(a[0] = max(b[0], c[0]) + 10; !a[a[0]]; a[0]--);
g = v = 0;
for (int i = 1; i <= a[0] + 10; i++)
{
g = a[i] * d + v;
v = g / 10;
f[e][i] = g % 10;
}
for(f[e][0] = a[0] + 10; !f[e][f[e][0]]; f[e][0]--);
}
int main()
{
scanf("%d", &n);
for(int i = 1, j; i <= n * n; i++) scanf ("%d", &j);
f[2][1] = f[2][0] = 1;
for(int i = 3; i <= n; i++)
opt(f[i - 1], f[i - 2], i - 1, i);
for (int i = f[n][0]; i >= 1; --i) printf("%d", f[n][i]);
}