把公共场所数量铺成 129×129 权重网格,覆盖正方形即二维矩形窗口;二维前缀和 O(1) 查询每个中心覆盖的数量之和,枚举全部 129² 个路口取最大并统计方案数。
OJ: roj
题目 ID: 3621
难度:普及+/提高-
标签:前缀和暴力枚举二维python
创建: 2026-10-02 10:54
更新: 2026-10-10 11:30
形式化题目
在一个
的带权点(覆盖正方形可以越出网格边界)。求覆盖权值之和最大的中心处,方案数(达到该最大和的中心个数)与最大和各是多少。
样例:1 30。
思路
朴素做法与规模评估。 最直接的想法:枚举每个路口
第一步:把覆盖判定变成矩形求和。 覆盖条件
第二步:二维前缀和,
外圈补一行一列
以下用样例画出任一窗口的四角查询(
| 中心 |
窗口(与网格求交后) | 覆盖到的点 | 覆盖和 |
|---|---|---|---|
| 无 |
再以中心
第三步:统计最大值与方案数。 扫描全部
与朴素做法的关系。 这里的枚举中心是题目语义自带的(答案就是所有中心里取最优),不枚举中心就没有"方案数"可言;优化发生在每次评估一个中心的开销上:从"逐点检查
代码
/**
* Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
* rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
* rainboy的学习导航网站: https://idx.roj.ac.cn
* create_at: 2026-10-10 11:30
* update_at: 2026-10-10 11:30
*/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=129;
int main(){ios::sync_with_stdio(false); cin.tie(nullptr); int d,n; if(!(cin>>d>>n)) return 0; vector<vector<int> > g(N+1, vector<int>(N+1)); for(int i=0;i<n;i++){int x,y,k;cin>>x>>y>>k; g[x][y]=k;} vector<vector<int> > P(N+1, vector<int>(N+1)); for(int i=0;i<N;i++) for(int j=0;j<N;j++) P[i+1][j+1]=g[i][j]+P[i][j+1]+P[i+1][j]-P[i][j]; int best=0,cnt=0; for(int cx=0;cx<N;cx++) for(int cy=0;cy<N;cy++){int x1=max(cx-d,0),x2=min(cx+d,N-1),y1=max(cy-d,0),y2=min(cy+d,N-1); int s=P[x2+1][y2+1]-P[x1][y2+1]-P[x2+1][y1]+P[x1][y1]; if(s>best){best=s;cnt=1;} else if(s==best) cnt++;} cout<<cnt<<' '<<best<<'\n'; return 0;}#!/usr/bin/env python3
# Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
# rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
# rainboy的学习导航网站: https://idx.roj.ac.cn
# create_at: 2026-10-02 10:54
# update_at: 2026-10-02 10:54
import sys
from itertools import accumulate
N = 129 # 街道编号 0..128
def solve() -> None:
data = iter(map(int, sys.stdin.buffer.read().split()))
d = next(data)
n = next(data)
# 权重网格:路口 (x, y) 上的公共场所数量(题目保证同一坐标只出现一次)
g = [[0] * (N + 1) for _ in range(N + 1)]
for _ in range(n):
x, y, k = next(data), next(data), next(data)
g[x][y] = k
# 二维前缀和:首行全 0 哨兵,逐行把行内前缀与上一行叠加
# P[i][j] = sum(g[0..i-1][0..j-1]),比手工双重循环短得多
P = [[0] * (N + 1)]
for row in g:
P.append([a + b for a, b in zip(accumulate(row, initial=0), P[-1])])
best = cnt = 0
# 枚举每个路口 (cx, cy) 作为发射器中心,正方形覆盖窗口用前缀和 O(1) 查询
for cx in range(N):
for cy in range(N):
# 覆盖正方形与网格求交(正方形可越出城市,但所有路口都在网格内)
x1, x2 = max(cx - d, 0), min(cx + d, N - 1)
y1, y2 = max(cy - d, 0), min(cy + d, N - 1)
s = P[x2 + 1][y2 + 1] - P[x1][y2 + 1] - P[x2 + 1][y1] + P[x1][y1]
if s > best:
best, cnt = s, 1
elif s == best:
cnt += 1
print(cnt, best)
if __name__ == "__main__":
solve()复杂度
- 时间复杂度:
——铺权重网格 ,二维前缀和 ,枚举 个中心每次 查询。 - 空间复杂度:
,权值矩阵与前缀和矩阵各一个。 - 最大覆盖和不超过
,Python 整数无溢出问题;本地实测 10 组数据每组约 ,远低于 OJ 限时。
总结
- 覆盖条件
就是"中心为 的正方形窗口",把点权铺成 网格后,问题变成"枚举全部中心 + 矩形窗口求和取最大"。 - 二维前缀和(外圈哨兵 + 容斥四角)把每次窗口查询降到
,窗口与网格求交由 max/min裁剪完成,边界零特判。 - 枚举中心是答案语义的一部分(要输出方案数),优化点在单次查询开销:
;统计时只在严格更大时重置,避免把 的空中心计入方案数。