首页 > 其他分享 >2024AcWing蓝桥杯集训·每日一题-前缀和

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

时间:2024-03-02 20:33:47浏览次数:30  
标签:10 Thanh int 美观 样例 2024AcWing 蓝桥 一题 墙体

1.[AcWing562.壁画]

题目描述

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

输入格式

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

输出格式

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

数据范围

\(1≤T≤100,\)
存在一个测试点 \(N=5∗10^6\),其他测试点均满足 \(2≤N≤100\)

输入样例
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=[A_1,A_2,…A_N],\)
\(B=[B_1,B_2,…B_N],\)
\(C=[C_1,C_2,…C_N],\)
请你统计有多少个三元组 \((i,j,k)\) 满足:
\(1≤i,j,k≤N\)
\(A_i<B_j<C_k\)

输入格式

第一行包含一个整数 \(N\)。
第二行包含 \(N\) 个整数 \(A_1,A_2,…A_N\)。
第三行包含 \(N\) 个整数 \(B_1,B_2,…B_N\)。
第四行包含 \(N\) 个整数 \(C_1,C_2,…C_N\)。

输出格式

一个整数表示答案。

数据范围

\(1≤N≤10^5,\)
\(0≤A_i,B_i,C_i≤10^5\)

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

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

  1. 二分。对 \(A\) 和 \(C\) 排序,对于每一个 \(B_i\) 在 \(A\) 中找到小于其的所有数的个数,在 \(C\) 中找到大于其的所有数的个数,这里进行数的查找可以使用二分。此方法的时间复杂度为 \(O(nlogn)\)。
  2. 前缀和。\(cnt\) 数组统计针对 \(A\) 和 \(C\) 统计每个数的个数,\(s\) 表示 \(cnt\) 的前缀和,即数 \(1\) 至 \(i\) 在 \(A\) 和 \(C\) 中的个数。
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,M\) 和 \(K\)。
之后 \(N\) 行每行包含 \(M\) 个整数,代表矩阵 \(A\)。

输出格式

一个整数代表答案。

数据范围

对于 \(30\%\) 的数据,\(N,M≤20\),
对于 \(70\%\) 的数据,\(N,M≤100\),
对于 \(100\%\) 的数据,\(1≤N,M≤500;0≤A_{ij}≤1000;1≤K≤2.5×10^8\)。

输入样例
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(n^4)\)。
如何优化?
利用双指针,限定子矩阵上下边界,两个指针指向左右边界,当左指针移动时,子矩阵的值必然减小。

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;
}

标签:10,Thanh,int,美观,样例,2024AcWing,蓝桥,一题,墙体
From: https://www.cnblogs.com/Cocoicobird/p/18049187

相关文章

  • 2024AcWing蓝桥杯集训·每日一题-二分
    1.[AcWing503.借教室]题目描述在大学期间,经常需要租借教室。大到院系举办活动,小到学习小组自习讨论,都需要向学校申请借教室。教室的大小功能不同,借教室人的身份不同,借教室的手续也不一样。面对海量租借教室的信息,我们自然希望编程解决这个问题。我们需要处理接下来\(n\)天......
  • 第十一届蓝桥杯试题E:排序
    目录题目分析验证代码题目对一个字符串,对它进行冒泡排序使其为升序,例如:对于lan,排序成aln需要交换一次(只能交换相邻的两个字母),对于qiao,排序成aioq就需要交换4次。请找出冒泡排序时恰好需要交换100次的字符串,如果有多个字符串满足条件,则找出最短的那个,如果有多个满足条件而且......
  • P8680 [蓝桥杯 2019 省 B] 特别数的和
    暴力秒了#include<bits/stdc++.h>#defineintlonglong//开longlong是个好习惯usingnamespacestd;boolbaozi(intx){while(x){intt=x%10;if(t==2||t==0||t==1||t==9)//数位判断{returntrue;......
  • 蓝桥杯2022年第十三届省赛真题-技能升级(中)
    目录题目题解:暴力题解:优化题目题解:暴力思路:枚举每一个Ai,并一直减去Bi,直到小于零为止,即为该技能所能增加的点数的集合。将每一个选择存进res中,并排序选择前M大的技能点即可。#首先,a-b加入列表,循环a/b次;其次,对列表排序,取前M个数进行求和a,b=map(int,input().split())#读入......
  • 2024 蓝桥杯模拟赛3(div1+div2)
    2024蓝桥杯模拟赛3(div1+div2)P8834[传智杯#3决赛]序列简单的模拟,数据范围很小,暴力即可点击查看代码#include<bits/stdc++.h>usingnamespacestd;typedeflonglongll;constintN=1e5+5;voidsolve(){ lln,k,a[N],cnt=0; cin>>n>>k; for(inti=1;i<=n;i++)c......
  • P8647 [蓝桥杯 2017 省 AB] 分巧克力
    题目链接:小巧克力的边长一定在\(1\sim10^5\)之间。答案为在\(1\sim10^5\)之间找一个最大的数,使得所有\(h[i]/a*w[i]/a\)的和\(\geqslantk\)即可。#include<cstdio>#include<algorithm>constintN=1e5+10;intn,k,h[N],w[N];boolcheck(inta)......
  • (蓝桥)递归与递推
    1、对于 scanf printf和cincout按照10^5来划分使用 递归实现指数型枚举 https://www.acwing.com/problem/content/94/ #include<iostream>#include<cstring>#include<cstdio>#include<algorithm>usingnamespacestd;constintN=17;intn;inta[N];......
  • 第十四届蓝桥杯个人题解
    a幸运的数主要是思路 遍历1-100000000每一层循环,首先将其每一位分到数组里,并记录位数,如果是偶数位再接着往下,比较前半和后半是否相等:通过加减最后结果是否为零来判断intmain(){  intnum=0;  for(inti=1;i<100000000;i++)  {    ints[9]={0};......
  • 备战蓝桥
    0地宫取宝-蓝桥云课(lanqiao.cn)对于该问题,首先是个迷宫问题,于是先考虑暴力求解,对于暴力来说,有这样一种方法:对于任何一点来说,都可以进行选或者不选,然后当走到终点时如果符合条件则答案加$1$,这样做的时间复杂度是$2^n也就是2^50$,很明显得不到满分.既然是迷宫那......
  • 2024 蓝桥杯模拟赛3(div1+div2)
    P8834[传智杯#3决赛]序列\(O(N^2)\)枚举defread():returnmap(int,input().split())n,k=read()a=list(read())res=0foriinrange(n):forjinrange(i):ifa[i]*a[j]<=k:res+=1print(res)P8780[蓝桥杯2022省......