【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]);
}
posted @ 2020-03-08 19:26  Jayun  阅读(114)  评论(0编辑  收藏  举报