题解:洛谷 P14976 Photoshoot

【题目来源】

洛谷:P14976 [USACO26JAN1] Photoshoot B - 洛谷

【题目描述】

农夫 John 正在一个神奇的牧场里观察他的奶牛,并希望拍摄他的奶牛的子集。

牧场可以看作一个 \(N \times N\) 的网格(\(1 \leq N \leq 500\)),每个位置站着一头静止的奶牛。农夫 John 的相机能够拍摄牧场中任意一个 \(K \times K\) 的正方形区域(\(1 \leq K \leq \min(N, 25)\))。

在任何时刻,每头奶牛都有一个介于 \(0\)\(10^6\) 之间的美丽值。一张照片的吸引力指数是照片中所有奶牛美丽值的总和。

每头奶牛的美丽值初始为 \(0\),因此一开始任何照片的吸引力指数都是 \(0\)

\(Q\) 个时刻(\(1 \leq Q \leq 3 \cdot 10^{4}\)),由于吃了农夫 John 牧场中种植的神奇牧草,一头奶牛的美丽值会增加一个正整数。

农夫 John 想知道在每次更新后,他能拍摄到的照片的最大吸引力指数是多少。

【输入】

第一行包含整数 \(N\)\(K\)

第二行包含一个整数 \(Q\)

接下来的 \(Q\) 行,每行包含三个整数:\(r\)\(c\)\(v\),分别表示行、列和新的美丽值(\(1 \leq r, c \leq N, 1 \leq v \leq 10^6\))。保证该位置的新美丽值大于该位置之前的美丽值。

【输出】

输出 \(Q\) 行,对应每次更新后照片的最大吸引力指数。

【输入样例】

4 2
3
2 2 11
3 4 3
3 1 100

【输出样例】

11
11
111

【核心思想】

  1. 问题分析:给定一个 \(N \times N\) 的网格(\(N \le 500\)),初始所有格子值为 0。有 \(Q\) 次单点更新操作(增加一个正整数),每次更新后,需要找出所有 \(K \times K\) 子正方形中(\(K \le 25\))总和的最大值。关键约束\(K\) 很小(\(\le 25\)),且 \(N\)\(Q\) 分别为 500 和 \(3 \times 10^4\)。直接每次更新后重新计算所有子正方形会超时(\(O(Q \cdot N^2 \cdot K^2)\))。但利用 \(K\) 小的特点,可以只更新受影响的 \(K \times K\) 窗口(最多 \(K^2 \le 625\) 个),维护每个窗口的和并实时更新最大值。

  2. 算法选择

    • 局部更新 + 维护最大值:用一个二维数组 s[i][j] 存储以 (i, j) 为左上角的 \(K \times K\) 窗口的和(\(1 \le i,j \le N-K+1\))。每次更新格子 (r, c) 时,所有左上角在 [r-K+1, r] × [c-K+1, c] 范围内的窗口都会受到影响(因为该格子属于这些窗口)。遍历这些窗口(最多 \(K^2\) 个),将它们的和加上增量 delta,并更新全局最大值 ans。这样每次更新复杂度为 \(O(K^2)\)\(Q \le 3\times 10^4\)\(K \le 25\),总操作约 \(1.9\times 10^7\),完全可行。
  3. 关键步骤

    • 读入\(N, K, Q\)
    • 初始化:网格值 a 全 0,窗口和 s 全 0,ans = 0
    • 处理每个更新
      1. 读入 (r, c, v)
      2. 计算增量 delta = v - a[r][c],并更新 a[r][c] = v
      3. 确定受影响窗口的左上角范围:行从 max(1, r-K+1)r(注意不能小于 1),列从 max(1, c-K+1)c
      4. 遍历这些左上角 (i, j)
        • s[i][j] += delta
        • s[i][j] > ans,则更新 ans = s[i][j]
      5. 输出 ans
  4. 时间/空间复杂度

    • 时间复杂度:\(O(Q \cdot K^2)\)\(K \le 25\)\(Q \le 3\times 10^4\),约 \(1.9\times 10^7\) 次操作,可接受。
    • 空间复杂度:\(O(N^2)\),存储矩阵和窗口和。
  5. 局部更新 + 维护最大值

    • 利用小窗口\(K\) 很小,因此每个点更新只影响有限个窗口,不需要重新计算所有窗口。
    • 增量更新:只记录窗口和的增量变化,避免重新求和。
    • 实时维护最大值:在更新窗口和的同时更新全局最大,避免每次扫描所有窗口。
    • 适用场景:适用于大规模网格中,小窗口查询最大值,且只有点更新的情况。

【解题思路】

【算法标签】

普及 #模拟

【代码详解】

#include <bits/stdc++.h>
using namespace std;

const int N = 505;  // 矩阵最大尺寸
int n, k, a[N][N], s[N][N];  // n: 矩阵大小,k: 窗口大小,a: 原始矩阵,s: 前缀和矩阵
int Q, r, c, v, ans = -1;  // Q: 查询次数,r,c: 坐标,v: 新值,ans: 最大窗口和

int main()
{
    cin >> n >> k >> Q;  // 读入矩阵大小、窗口大小、操作次数

    while (Q--)  // 处理每个更新操作
    {
        cin >> r >> c >> v;  // 读入要更新的位置和新值

        // 计算新值与旧值的差值
        int delta = v - a[r][c];
        a[r][c] = v;  // 更新原始矩阵

        // 更新所有受影响的k×k窗口的和
        // 只有左上角在[r-k+1, r]×[c-k+1, c]范围内的窗口会受到影响
        for (int i = max(1, r - k + 1); i <= r; i++)
        {
            for (int j = max(1, c - k + 1); j <= c; j++)
            {
                // 更新该窗口的和
                s[i][j] += delta;
                // 更新全局最大值
                ans = max(ans, s[i][j]);
            }
        }

        // 输出当前最大k×k窗口和
        cout << ans << endl;
    }

    return 0;
}

【运行结果】

4 2
3
2 2 11
11
3 4 3
11
3 1 100
111
posted @ 2026-08-29 09:37  团爸讲算法  阅读(4)  评论(0)    收藏  举报