2024AcWing蓝桥杯集训·每日一题-前缀和

1.[AcWing562.壁画]

题目描述

Thanh 想在一面被均分为 N 段的墙上画一幅精美的壁画。
每段墙面都有一个美观评分,这表示它的美观程度(如果它的上面有画的话)。
不幸的是,由于洪水泛滥,墙体开始崩溃,所以他需要加快他的作画进度!
每天 Thanh 可以绘制一段墙体。
在第一天,他可以自由的选择任意一段墙面进行绘制。
在接下来的每一天,他只能选择与绘制完成的墙面相邻的墙段进行作画,因为他不想分开壁画。
在每天结束时,一段未被涂颜料的墙将被摧毁(Thanh 使用的是防水涂料,因此涂漆的部分不能被破坏),且被毁掉的墙段一定只与一段还未被毁掉的墙面相邻。
Thanh 的壁画的总体美观程度将等于他作画的所有墙段的美观评分的总和。
Thanh 想要保证,无论墙壁是如何被摧毁的,他都可以达到至少 B 的美观总分。
请问他能够保证达到的美观总分 B 的最大值是多少。

输入格式

第一行包含整数 T,表示共有 T 组测试数据。
每组数据的第一行包含整数 N
第二行包含一个长度为 N 的字符串,字符串由数字 09 构成,第 i 个字符表示第 i 段墙面被上色后能达到的美观评分。

输出格式

每组数据输出一个结果,每个结果占一行。
结果表示为 Case #x: y,其中 x 为组别编号(从 1 开始),y 为 Thanh 可以保证达到的美观评分的最大值。

数据范围

1T100,
存在一个测试点 N=5106,其他测试点均满足 2N100

输入样例
4
4
1332
4
9583
3
616
10
1029384756
输出样例
Case #1: 6
Case #2: 14
Case #3: 7
Case #4: 31
样例解释

在第一个样例中,无论墙壁如何被破坏,Thanh 都可以获得 6 分的美观总分。在第一天,他可以随便选一个美观评分 3 的墙段进行绘画。在一天结束时,第一部分或第四部分将被摧毁,但无论哪一部分都无关紧要。在第二天,他都可以在另一段美观评分 3 的墙段上作画。

在第二个样例中,Thanh 在第一天选择最左边的美观评分为 9 的墙段上作画。在第一天结束时唯一可以被毁掉的墙体是最右边的那段墙体,因为最左边的墙壁被涂上了颜料。在第二天,他可以选择在左数第二段评分为 5 的墙面上作画。然后右数第二段墙体被摧毁。请注意,在第二天,Thanh 不能选择绘制第三段墙面,因为它不与任何其他作画墙面相邻。这样可以获得 14 分的美观总分。

解题思路

因为每次被摧毁的墙体都只与一个未被破坏的墙体相邻,因此每次被摧毁的墙体只能是两侧的墙体,因此根据此性质确定最后绘画的墙体长度。根据固定区间长度求解最大值,利用前缀和预处理。

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.递增三元组]

题目描述

给定三个整数数组
A=[A1,A2,AN],
B=[B1,B2,BN],
C=[C1,C2,CN],
请你统计有多少个三元组 (i,j,k) 满足:
1i,j,kN
Ai<Bj<Ck

输入格式

第一行包含一个整数 N
第二行包含 N 个整数 A1,A2,AN
第三行包含 N 个整数 B1,B2,BN
第四行包含 N 个整数 C1,C2,CN

输出格式

一个整数表示答案。

数据范围

1N105,
0Ai,Bi,Ci105

输入样例
3
1 1 1
2 2 2
3 3 3
输出样例
27
解题思路

提供两种思路,两种思路都是以 B 数组作为连接。

  1. 二分。对 AC 排序,对于每一个 BiA 中找到小于其的所有数的个数,在 C 中找到大于其的所有数的个数,这里进行数的查找可以使用二分。此方法的时间复杂度为 O(nlogn)
  2. 前缀和。cnt 数组统计针对 AC 统计每个数的个数,s 表示 cnt 的前缀和,即数 1iAC 中的个数。
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.统计子矩阵]

题目描述

给定一个 N×M 的矩阵 A,请你统计有多少个子矩阵 (最小 1×1,最大 N×M) 满足子矩阵中所有数的和不超过给定的整数 K?

输入格式

第一行包含三个整数 N,MK
之后 N 行每行包含 M 个整数,代表矩阵 A

输出格式

一个整数代表答案。

数据范围

对于 30% 的数据,N,M20
对于 70% 的数据,N,M100
对于 100% 的数据,1N,M500;0Aij1000;1K2.5×108

输入样例
3 4 10
1 2 3 4
5 6 7 8
9 10 11 12
输出样例
19
样例解释

满足条件的子矩阵一共有 19,包含:
大小为 1×1 的有 10 个。
大小为 1×2 的有 3 个。
大小为 1×3 的有 2 个。
大小为 1×4 的有 1 个。
大小为 2×1 的有 3 个。

解题思路

最简单的思路,二维前缀和预处理,枚举出每一个子矩阵进行判断,这样时间复杂度是 O(n4)
如何优化?
利用双指针,限定子矩阵上下边界,两个指针指向左右边界,当左指针移动时,子矩阵的值必然减小。

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;
}
posted @   Cocoicobird  阅读(39)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架
历史上的今天:
2023-03-02 数据结构1.1-单调栈
点击右上角即可分享
微信分享提示