题解:洛谷 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
【核心思想】
-
问题分析:给定一个 \(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\) 个),维护每个窗口的和并实时更新最大值。
-
算法选择:
- 局部更新 + 维护最大值:用一个二维数组
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\),完全可行。
- 局部更新 + 维护最大值:用一个二维数组
-
关键步骤:
- 读入:\(N, K, Q\)。
- 初始化:网格值
a全 0,窗口和s全 0,ans = 0。 - 处理每个更新:
- 读入
(r, c, v)。 - 计算增量
delta = v - a[r][c],并更新a[r][c] = v。 - 确定受影响窗口的左上角范围:行从
max(1, r-K+1)到r(注意不能小于 1),列从max(1, c-K+1)到c。 - 遍历这些左上角
(i, j):s[i][j] += delta。- 若
s[i][j] > ans,则更新ans = s[i][j]。
- 输出
ans。
- 读入
-
时间/空间复杂度:
- 时间复杂度:\(O(Q \cdot K^2)\),\(K \le 25\),\(Q \le 3\times 10^4\),约 \(1.9\times 10^7\) 次操作,可接受。
- 空间复杂度:\(O(N^2)\),存储矩阵和窗口和。
-
局部更新 + 维护最大值:
- 利用小窗口:\(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
浙公网安备 33010602011771号