校内赛 T1 寻路

题目描述

简要题意:给定一个 $n\times n$ 的矩阵 $a$,$a$ 中每个位置上有一个数。你要从左上角 $(1,1)$ 走到右下角 $(n,n)$,每次只能向下或者向右走。将沿途的格子上的数组依次记录下来,可以得到一个长度为 $2n-1$ 的序列,选择的路线不同,可能会得到不同的序列,要求对于每一个序列 $Q$,计算有多少条路线对应这个序列 $Q$,路线的个数记为 $f(Q)$。求 $\sum f^2(Q)$

$n\le 300$

Solution

首先我们尝试将平方给干掉,假设 $f(Q)$ 为 $x$,那么它的贡献为 $x^2$

我们将其认为对于每条在 $x$ 中的路线,它与其它属于 $x$ 的路线都记一次贡献,那么一共还是 $x^2$ 次

那么我们现在得到了另一种计算贡献的方式,相当于两个人从 $(1,1)$ 走到 $(n,n)$,得到序列相同的走法

这个东西显然可以 $O(n^2)$ $dp$

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include <iostream>
#include <cstdio>
#define maxn 310
using namespace std;

int n, a[maxn][maxn];

int dx[] = { 0, 1 };
int dy[] = { 1, 0 };

const int p = 1000000007;

inline int add(int x, int y) { return x + y >= p ? x + y - p : x + y; }

int f[2 * maxn][maxn][maxn];
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr); cout.tie(nullptr);

cin >> n;
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= n; ++j) cin >> a[i][j];
f[1][1][1] = 1;
for (int k = 1; k <= 2 * n - 2; ++k)
for (int x1 = 1, y1 = k - x1 + 1; x1 <= min(n, k); ++x1, --y1)
for (int x2 = 1, y2 = k - x2 + 1; x2 <= min(n, k); ++x2, --y2)
for (int i = 0; i < 2; ++i) {
if (y1 < 0 || y1 > n || y2 < 0 || y2 > n) continue;
int X1 = x1 + dx[i], Y1 = y1 + dy[i];
if (X1 > n || Y1 > n) continue ;
for (int j = 0; j < 2; ++j) {
int X2 = x2 + dx[j], Y2 = y2 + dy[j];
if (X2 > n || Y2 > n) continue ;
if (a[X1][Y1] ^ a[X2][Y2]) continue ;
f[k + 1][X1][X2] = add(f[k + 1][X1][X2], f[k][x1][x2]);
}
}
cout << f[2 * n - 1][n][n] << endl;
return 0;
}