题解 P7472 [NOI Online 2021 入门组] 吃豆人

原题

首先看数据范围 $n\le10^3$,那么想到是一个 $O(n^3)$ 或 $O(n^2)$ 的做法。

画图观察一下,发现对于每个边上的点,是具有确定性的,无论从哪个方向出发,均有且仅有一种矩阵能包含边上的点,于是考虑以边上的点确定每一个不同的矩阵。

容易看出每个点出发的矩阵都会绕一圈回到该点,每个矩阵边上的点数量不超过 $2(n-1)$。于是可用第一行的每个点来表示经过该点的矩阵并预处理出每一个矩阵经过的点权之和,时间复杂度为 $O(n^2)$。

需要注意会有线状的矩阵,不能重复加两次,需要特判一下。

枚举任意两个矩阵,发现所选的两个矩阵还可能会有重复的部分需要减去,这里需要分类讨论。

设选择的矩阵分别包含第一行的第 $i$ 个和第 $j$ 个点,不妨设 $j>i$,$c_x$ 表示第一行第 $x$ 个点对应的矩阵点权之和,则有以下结论:

  1. 当 $j-i$ 为奇数时,$ans=c_i+c_j$。此时这两个矩阵不会有公共点,原因是它们的中点不是整数,其交点纵坐标也不是整数。

  2. 当 $j-i$ 为偶数且 $i=1,j=n$ 时,$ans=c_i+c_j-a_{\frac{n+1}{2},\frac{n+1}{2}}$。两个矩阵皆为线状,只有一个交点在矩阵中心。

  3. 当 $j-i$ 为偶数且 $i=1$ 和 $j=n$ 仅满足任意一条件时,$ans=c_i+c_j-a_{1+\frac{j-i}{2},\frac{i+j}{2}}-a_{n-\frac{j-i}{2},n-\frac{i+j}{2}+1}$。这是仅一个矩阵为线状时的情况,结论貌似很难懂,但画画图就发现纵坐标和中点有关,然后就很好理解了。

  4. 当 $j-i$ 为偶数且 $i\not=1,j\not=n$ 时,$ans=c_i+c_j-a_{1+\frac{j-i}{2},\frac{i+j}{2}}-a_{n-\frac{j-i}{2},n-\frac{i+j}{2}+1}-a_{\frac{i+j}{2},1+\frac{j-i}{2}}-a_{n-\frac{i+j}{2}+1,n-\frac{j-i}{2}}$。实质上就是满足了正方形的对称性,互换了下横纵坐标即为另外两个的交点。

总体时间复杂度为 $O(n^2)$,这个数据范围稳过。

#include <bits/stdc++.h>
using namespace std;
const int kx[] = {1, 1, -1, -1}, ky[] = {-1, 1, 1, -1};
const int N = 1e3 + 10;
int n, a[N][N], c[N], ans;
bool vis[N][N];
int main() {
//  freopen("pacman.in", "r", stdin);
//  freopen("pacman.out", "w", stdout);
    scanf("%d", &n);
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= n; j++)
            scanf("%d", &a[i][j]);
    for(int i = 1; i <= n; i++)
        c[1] += a[i][i], c[n] += a[i][n - i + 1];
    for(int i = 2; i < n; i++) {
        int x = 1, y = i, op = 0, sum = 0;
        bool flag = false;
        for(;;) {
            if(x == 1 && y == i && flag) {
                c[i] = sum;
                break;
            }
            sum += a[x][y];
            flag = true;
            if(x + kx[op] <= 0 || x + kx[op] > n || y + ky[op] <= 0 || y + ky[op] > n)
                op++;
            x += kx[op], y += ky[op];
        }
    }
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= n; j++) {
            if(i == j)
                continue;
            int mx = max(i, j), mn = min(i, j);
            if((mx - mn) & 1) {
                ans = max(ans, c[i] + c[j]);
            }
            else if(mn == 1 && mx == n) {
                int t = c[i] + c[j] - a[(i + j) / 2][(i + j) / 2];
                ans = max(ans, t);
            }
            else {
                int t = c[i] + c[j];
                t -= a[1 + (mx - mn) / 2][(i + j) / 2];
                t -= a[n - (mx - mn) / 2][n - (i + j) / 2 + 1]; 
                if(mn == 1 || mx == n)
                    ans = max(ans, t);
                else {
                    t -= a[(i + j) / 2][1 + (mx - mn) / 2];
                    t -= a[n - (i + j) / 2 + 1][n - (mx - mn) / 2];
                    ans = max(ans, t);
                }
            }
        }
    printf("%d", ans);
    return 0;
}
posted @ 2021-03-28 00:23  Terac  阅读(17)  评论(0)    收藏  举报  来源