2024AcWing蓝桥杯集训·每日一题-前缀和
1.[AcWing562.壁画]
题目描述
Thanh 想在一面被均分为
每段墙面都有一个美观评分,这表示它的美观程度(如果它的上面有画的话)。
不幸的是,由于洪水泛滥,墙体开始崩溃,所以他需要加快他的作画进度!
每天 Thanh 可以绘制一段墙体。
在第一天,他可以自由的选择任意一段墙面进行绘制。
在接下来的每一天,他只能选择与绘制完成的墙面相邻的墙段进行作画,因为他不想分开壁画。
在每天结束时,一段未被涂颜料的墙将被摧毁(Thanh 使用的是防水涂料,因此涂漆的部分不能被破坏),且被毁掉的墙段一定只与一段还未被毁掉的墙面相邻。
Thanh 的壁画的总体美观程度将等于他作画的所有墙段的美观评分的总和。
Thanh 想要保证,无论墙壁是如何被摧毁的,他都可以达到至少
请问他能够保证达到的美观总分
输入格式
第一行包含整数
每组数据的第一行包含整数
第二行包含一个长度为
输出格式
每组数据输出一个结果,每个结果占一行。
结果表示为 Case #x: y
,其中
数据范围
存在一个测试点
输入样例
4
4
1332
4
9583
3
616
10
1029384756
输出样例
Case #1: 6
Case #2: 14
Case #3: 7
Case #4: 31
样例解释
在第一个样例中,无论墙壁如何被破坏,Thanh 都可以获得
在第二个样例中,Thanh 在第一天选择最左边的美观评分为
解题思路
因为每次被摧毁的墙体都只与一个未被破坏的墙体相邻,因此每次被摧毁的墙体只能是两侧的墙体,因此根据此性质确定最后绘画的墙体长度。根据固定区间长度求解最大值,利用前缀和预处理。
C++代码
#include <bits/stdc++.h>
using namespace std;
const int N = 5e6;
int T;
int sum[N];
int main() {
cin >> T;
for (int i = 1; i <= T; i++) {
int n;
string S;
cin >> n >> S;
// 前缀和预处理
for (int j = 1; j <= n; j++) sum[j] = sum[j - 1] + S[j - 1] - '0';
// 区间长度
int m = (n + 1) / 2;
int maxv = 0;
for (int j = 1; j + m - 1 <= n; j++)
maxv = max(maxv, sum[j + m - 1] - sum[j - 1]);
cout << "Case #" << i << ": " << maxv << "\n";
}
return 0;
}
2.[AcWing1236.递增三元组]
题目描述
给定三个整数数组
请你统计有多少个三元组
输入格式
第一行包含一个整数
第二行包含
第三行包含
第四行包含
输出格式
一个整数表示答案。
数据范围
输入样例
3
1 1 1
2 2 2
3 3 3
输出样例
27
解题思路
提供两种思路,两种思路都是以
- 二分。对
和 排序,对于每一个 在 中找到小于其的所有数的个数,在 中找到大于其的所有数的个数,这里进行数的查找可以使用二分。此方法的时间复杂度为 。 - 前缀和。
数组统计针对 和 统计每个数的个数, 表示 的前缀和,即数 至 在 和 中的个数。
C++代码
#include <bits/stdc++.h>
using namespace std;
const int N = 100010;
typedef long long LL;
int n;
int a[N], b[N], c[N];
int cnt[N], s[N];
int as[N], cs[N];
int main() {
scanf("%d", &n);
// 自增是为了方便前缀和计算
for (int i = 1; i <= n; i++) scanf("%d", &a[i]), a[i]++;
for (int i = 1; i <= n; i++) scanf("%d", &b[i]), b[i]++;
for (int i = 1; i <= n; i++) scanf("%d", &c[i]), c[i]++;
for (int i = 1; i <= n; i++) cnt[a[i]]++;
// s[i] 表示数组 a 中从 0 到 i 的数的个数
for (int i = 1; i < N; i++) s[i] = s[i - 1] + cnt[i];
for (int i = 1; i <= n; i++) as[i] = s[b[i] - 1];
memset(cnt, 0, sizeof cnt);
memset(s, 0, sizeof s);
for (int i = 1; i <= n; i++) cnt[c[i]]++;
for (int i = 1; i < N; i++) s[i] = s[i - 1] + cnt[i];
for (int i = 1; i <= n; i++) cs[i] = s[N - 1] - s[b[i]];
LL res = 0;
for (int i = 1; i <= n; i++)
res += (LL) as[i] * cs[i];
printf("%ld", res);
/*
// 二分解法
sort(a + 1, a + n + 1);
sort(c + 1, c + n + 1);
LL res = 0;
for (int i = 1; i <= n; i++) {
int l1 = 1, r1 = n;
while (l1 < r1) {
int mid = l1 + r1 + 1 >> 1;
if (a[mid] < b[i]) l1 = mid;
else r1 = mid - 1;
}
int l2 = 1, r2 = n;
while (l2 < r2) {
int mid = l2 + r2 >> 1;
if (c[mid] > b[i]) r2 = mid;
else l2 = mid + 1;
}
if (a[r1] >= b[i] || c[r2] <= b[i])
continue;
res += (LL) r1 * (n - r2 + 1);
}
printf("%ld", res);
*/
return 0;
}
3.[AcWing4405.统计子矩阵]
题目描述
给定一个
输入格式
第一行包含三个整数
之后
输出格式
一个整数代表答案。
数据范围
对于
对于
对于
输入样例
3 4 10
1 2 3 4
5 6 7 8
9 10 11 12
输出样例
19
样例解释
满足条件的子矩阵一共有
大小为
大小为
大小为
大小为
大小为
解题思路
最简单的思路,二维前缀和预处理,枚举出每一个子矩阵进行判断,这样时间复杂度是
如何优化?
利用双指针,限定子矩阵上下边界,两个指针指向左右边界,当左指针移动时,子矩阵的值必然减小。
C++代码
#include <iostream>
using namespace std;
const int N = 510;
typedef long long LL;
int n, m, k;
int a[N][N];
LL s[N][N];
int main() {
scanf("%d%d%d", &n, &m, &k);
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++) {
scanf("%d", &a[i][j]);
a[i][j] = a[i - 1][j] + a[i][j - 1] - a[i - 1][j - 1] + a[i][j];
}
LL res = 0;
for (int i = 1; i <= m; i++) // 上边界
for (int j = i; j <= m; j++) // 下边界
for (int l = 1, r = 1; r <= n; r++) { // 左右边界
while (l <= r && a[r][j] - a[l - 1][j] - a[r][i - 1] + a[l - 1][i - 1] > k)
l++;
if (l <= r)
res += r - l + 1;
}
printf("%lld", res);
return 0;
}
本文来自博客园,作者:Cocoicobird,转载请注明原文链接:https://www.cnblogs.com/Cocoicobird/p/18049187
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架
2023-03-02 数据结构1.1-单调栈