news 2025/12/17 16:55:16

P2692 覆盖

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
P2692 覆盖

记录46

#include<bits/stdc++.h> using namespace std; int main(){ int a[5010]={},c[5010]={}; int n,m,b,g,s,e,cnt=0,cnt_x=0; cin>>n>>m>>b>>g; while(b--){ cin>>s>>e; for(int i=s;i<=e;i++) a[i]=1; } while(g--){ cin>>s>>e; for(int i=s;i<=e;i++) c[i]=1; } for(int i=1;i<=n;i++){ if(a[i]==1) cnt+=m; else cnt_x++; } for(int i=1;i<=m;i++){ if(c[i]==1) cnt+=cnt_x; } cout<<cnt; return 0; }

题目传送门https://www.luogu.com.cn/problem/P2692


突破点

每个男生负责打扫一些连续的行,

👉对行进行操作

每个女生负责打扫一些连续的列。

👉对列进行操作


思路

如果直接二维数组铺开,很显然会TLE超时,所以需要转换思路

  1. 标记出现的行数组跟列数组的编号
  2. 统计行数组,如果打扫过就加上列数
  3. 统计列数组,如果打扫过就加上未被打扫的行数(列行重复的部分不需要打扫)

注意:统计列数时,加上未被打扫的行数,是因为这一列中,有些被行打扫完毕


代码简析

#include<bits/stdc++.h> using namespace std; int main(){ int a[5010]={},c[5010]={}; int n,m,b,g,s,e,cnt=0,cnt_x=0; cin>>n>>m>>b>>g; while(b--){ cin>>s>>e; for(int i=s;i<=e;i++) a[i]=1; } while(g--){ cin>>s>>e; for(int i=s;i<=e;i++) c[i]=1; } ... ... return 0; }

n(行),m(列),b(男生数),g(女生数),

s(开始打扫编号),e(结束打扫编号),

cnt=0(统计一共打扫的格子),cnt_x=0(未被打扫的行数)

两个while()循环👉记录行列的打扫情况

#include<bits/stdc++.h> using namespace std; int main(){ int a[5010]={},c[5010]={}; int n,m,b,g,s,e,cnt=0,cnt_x=0; cin>>n>>m>>b>>g; while(b--){ cin>>s>>e; for(int i=s;i<=e;i++) a[i]=1; } while(g--){ cin>>s>>e; for(int i=s;i<=e;i++) c[i]=1; } for(int i=1;i<=n;i++){ if(a[i]==1) cnt+=m; else cnt_x++; } for(int i=1;i<=m;i++){ if(c[i]==1) cnt+=cnt_x; } cout<<cnt; return 0; }

for(int i=1;i<=n;i++){}👉遍历行数

if(a[i]==1) cnt+=m;👉这一行被打扫,加上这一行的格子(即列数)

else cnt_x++;👉记录下来没被打扫的行数

for(int i=1;i<=m;i++){}👉遍历列数

if(c[i]==1) cnt+=cnt_x;👉这一列被打扫,统计除行打扫外的部分


补充

在CSP中遍历超大二维数组(如10⁴×10⁴)时,O(n²)暴力遍历必然超时。以下是系统性优化思路,按优先级排序:


1. 前缀和/差分(O(1)区间查询)

场景:频繁查询子矩阵和、最大值、最小值

暴力问题

// 每次查询O(n²),Q次查询O(n²Q) = 10¹²,必TLE for (int i = x1; i <= x2; i++) for (int j = y1; j <= y2; j++) sum += a[i][j];

优化后

// 预处理O(n²),每次查询O(1) int pre[N][N]; // pre[i][j] = Σa[1..i][1..j] pre[i][j] = a[i][j] + pre[i-1][j] + pre[i][j-1] - pre[i-1][j-1]; // 查询子矩阵和O(1) int sum = pre[x2][y2] - pre[x1-1][y2] - pre[x2][y1-1] + pre[x1-1][y1-1];

时间:从O(n²Q)降到O(n² + Q)


2. 降维打击(转化为一维问题)

场景:二维DP、最值问题

暴力问题

// 二维DP:O(n²m²) = 10⁸,边界 for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) for (int k = 0; k < i; k++) for (int l = 0; l < j; l++) dp[i][j] = max(dp[i][j], dp[k][l] + cost);

优化后

// 将每行/每列压缩,转化为一维线段树DP: O(n*m log m) // 或扫描线思想: O(n*m) for (int i = 1; i <= n; i++) { // 对每一行用单调队列/线段树维护最值 for (int j = 1; j <= m; j++) { dp[i][j] = query_max(i, j) + a[i][j]; } }

3. 分块处理(BLock Decomposition)

场景:区间修改+区间查询,但数据不全是10⁴×10⁴

思想:将n×n矩阵分成√n×√n块,每块单独处理

int block = sqrt(n); for (int bx = 0; bx < n; bx += block) for (int by = 0; by < n; by += block) // 每块内部暴力,块间O(1)处理 process_block(bx, by);

复杂度O(n²/block² × block²) = O(n²),但常数降低,缓存友好


4. 数据结构优化(树状数组/线段树)

场景:动态二维区间查询/修改

暴力问题

// 单点更新+区间查询:每次O(n²),Q次O(n²Q) = 10¹² update(x, y, val); query(x1, y1, x2, y2);

优化后

// 二维树状数组:每次O(log²n),Q次O(Q log²n) = 10⁶ void update(int x, int y, int val) { for (int i = x; i <= n; i += i & -i) for (int j = y; j <= n; j += j & -j) bit[i][j] += val; } int query(int x, int y) { int sum = 0; for (int i = x; i > 0; i -= i & -i) for (int j = y; j > 0; j -= j & -j) sum += bit[i][j]; return sum; }

5. 方向性优化(减少遍历范围)

场景:只关心特定方向(如右下、左上)

暴力问题

// 四个方向遍历:4×n×m = 4×10⁸,超时 for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) for (int dir = 0; dir < 4; dir++) check(i+dx[dir], j+dy[dir]);

优化后

// 预处理每个方向的前缀:O(n×m) // 查询时O(1) for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { right[i][j] = right[i][j-1] + 1; // 向右连续1数量 down[i][j] = down[i-1][j] + 1; // 向下连续1数量 } } // 查询时直接读取,无需遍历

6. 贪心/双指针(跳过无效遍历)

场景:单调性问题

暴力问题

// 每行找最右0:O(n²) for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { if (a[i][j] == 0) rightmost[i] = j; } }

优化后

// 从右往左一次扫描:O(n×m) for (int i = 1; i <= n; i++) { for (int j = m; j >= 1; j--) { // 从右往左 if (a[i][j] == 0) { rightmost[i] = j; break; // 找到后立即跳出 } } } // 或每行用二分:O(n log m)

7. 缓存友好优化(空间换时间)

场景:行列交替访问,缓存命中率低

原始问题

// 访问a[i][j]和a[j][i],缓存抖动,速度慢3倍 for (int i = 0; i < n; i++) for (int j = 0; j < n; j++) sum += a[i][j] + a[j][i];

优化后

// 复制转置矩阵:O(n²),但后续访问缓存友好 int b[N][N]; // b是a的转置 for (int i = 0; i < n; i++) for (int j = 0; j < n; j++) b[j][i] = a[i][j]; // 后续操作:连续内存访问,速度快 for (int i = 0; i < n; i++) for (int j = 0; j < n; j++) sum += a[i][j] + b[i][j];

8. 竞赛通用决策树

是否需要遍历整个二维数组? ├── 是 → 能否用前缀和/差分转为O(1)查询? │ ├── 能 → 预处理O(n²) + O(1)查询 │ └── 否 → 降维/分块/数据结构优化 │ ├── 否 → 能否用贪心/双指针减少遍历范围? │ ├── 能 → 局部遍历O(n)或O(n log n) │ └── 否 → 方向性优化或缓存优化

9. 一句话总结

暴力遍历O(n²)是万恶之源,CSP-J/S中优先往前缀和、降维、数据结构三条路想,能O(1)不O(n),能O(n)不O(n²)。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2025/12/12 16:35:14

P3392 涂条纹

记录47 #include<bits/stdc.h> using namespace std; int main(){int n,m,w[55]{},b[55]{},r[55]{},cnt0;int cntW0,cntB0,cntR0;char c;cin>>n>>m;for(int i1;i<n;i){for(int j1;j<m;j){cin>>c;if(cW) w[i];if(cB) b[i];if(cR) r[i];}w[i]w[i-…

作者头像 李华
网站建设 2025/12/12 16:35:14

传统SEO需要3-6个月,为什么部分企业选择技术路径实现快速见效?

传统SEO通常需要3-6个月才能看到效果&#xff0c;这个周期对很多企业来说太长了。现在有些企业开始用技术手段缩短这个周期&#xff0c;比如生成式引擎优化&#xff08;GEO&#xff09;和AI驱动的内容优化。这篇文章聊聊为什么会出现这种变化&#xff0c;以及技术路径能带来什么…

作者头像 李华
网站建设 2025/12/12 16:35:04

Cursor试用限制突破方案:多窗口智能管理技术深度解析

还在为Cursor AI编程助手的试用限制而苦恼吗&#xff1f;当你正沉浸在代码创作的灵感迸发中&#xff0c;突然弹出的"试用请求已达上限"提示是否让你的工作戛然而止&#xff1f;别担心&#xff0c;今天我们将为你呈现一套全新的智能解决方案&#xff0c;让你彻底告别C…

作者头像 李华
网站建设 2025/12/17 10:56:44

彻底解决苹果蝴蝶键盘双击问题:Unshaky完整使用指南

彻底解决苹果蝴蝶键盘双击问题&#xff1a;Unshaky完整使用指南 【免费下载链接】Unshaky A software attempt to address the "double key press" issue on Apples butterfly keyboard [not actively maintained] 项目地址: https://gitcode.com/gh_mirrors/un/Un…

作者头像 李华