备战蓝桥杯-前缀和(附多道题解分析)

备战蓝桥杯-前缀和(附多道题解分析),第1张

备战蓝桥杯-前缀和(附多道题解分析) 前缀和经典例题汇总

递推与递归详解

二分算法详解

前缀和模板题

输入一个长度为 nn 的整数序列。

接下来再输入 mm 个询问,每个询问输入一对 l,rl,r。

对于每个询问,输出原序列中从第 ll 个数到第 rr 个数的和。

输入格式

第一行包含两个整数 nn 和 mm。

第二行包含 nn 个整数,表示整数数列。

接下来 mm 行,每行包含两个整数 ll 和 rr,表示一个询问的区间范围。

输出格式

共 mm 行,每行输出一个询问的结果。

数据范围

1≤l≤r≤n1≤l≤r≤n,
1≤n,m≤1000001≤n,m≤100000,
−1000≤数列中元素的值≤1000−1000≤数列中元素的值≤1000

输入样例:
5 3
2 1 3 6 4
1 2
1 3
2 4
输出样例:
3
6
10

板子,注意下标的判断

#include 
#include 
#include 

using namespace std;

const int N = 100005;
int a[N], sum[N];
int n, m;

int main()
{
    int left, right, value;
    cin >> n >> m;
    cin >> a[0];
    sum[0] = a[0];
    for (int i = 1; i < n; i++) {
        cin >> a[i];
        sum[i] = sum[i - 1] + a[i];
    }
    while (m -- ) {
        cin >> left >> right;
        //第left 到 第right
        value = sum[right - 1] - sum[left - 2];
        cout << value << endl;
    }
    return 0;
} 

二维前缀和模板

输入一个 nn 行 mm 列的整数矩阵,再输入 qq 个询问,每个询问包含四个整数 x1,y1,x2,y2x1,y1,x2,y2,表示一个子矩阵的左上角坐标和右下角坐标。

对于每个询问输出子矩阵中所有数的和。

输入格式

第一行包含三个整数 n,m,qn,m,q。

接下来 nn 行,每行包含 mm 个整数,表示整数矩阵。

接下来 qq 行,每行包含四个整数 x1,y1,x2,y2x1,y1,x2,y2,表示一组询问。

输出格式

共 qq 行,每行输出一个询问的结果。

数据范围

1≤n,m≤10001≤n,m≤1000,
1≤q≤2000001≤q≤200000,
1≤x1≤x2≤n1≤x1≤x2≤n,
1≤y1≤y2≤m1≤y1≤y2≤m,
−1000≤矩阵内元素的值≤1000−1000≤矩阵内元素的值≤1000

输入样例:
3 4 3
1 7 2 4
3 6 2 8
2 1 2 3
1 1 2 2
2 1 3 4
1 3 3 4
输出样例:
17
27
21
#include 
#include 
#include 

using namespace std;

const int N = 1010;
int a[N][N], sum[N][N];
int n, m, t;
int main()
{
    cin >> n >> m >> t;
    for (int i = 1; i <= n; i ++ )
        for (int j = 1; j <= m; j ++ )
        {
            cin >> a[i][j];
            sum[i][j] = sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1] + a[i][j];
        }
    while (t -- )
    {
        int x1, x2, y1, y2;
        cin >> x1 >> y1 >> x2 >> y2;
        int value = sum[x2][y2] - sum[x1 - 1][y2] - sum[x2][y1 - 1] + sum[x1 - 1][y1 - 1];
        cout << value << endl;
    }
}
K倍区间(蓝桥杯真题)

给定一个长度为 NN 的数列,A1,A2,…ANA1,A2,…AN,如果其中一段连续的子序列 Ai,Ai+1,…AjAi,Ai+1,…Aj 之和是 KK 的倍数,我们就称这个区间 [i,j][i,j] 是 KK 倍区间。

你能求出数列中总共有多少个 KK 倍区间吗?

输入格式

第一行包含两个整数 NN 和 KK。

以下 NN 行每行包含一个整数 AiAi。

输出格式

输出一个整数,代表 KK 倍区间的数目。

数据范围

1≤N,K≤1000001≤N,K≤100000,
1≤Ai≤1000001≤Ai≤100000

输入样例:
5 2
1
2
3
4
5
输出样例:
6

分析:求区间是k的倍数的数量,假设s[0] % k == 1 ,s[3] % k ==1 那么从区间 1 - 3的和就是k的倍数,因为s[3]与s[1]的余数是相同的

如果相减的话就一定是k的倍数,因此我们在便利的时候,检查一下我们前面的前缀和中有没有与s[cur] % k的数是相同的,如果有,那么两者之间就可以构成k的倍数。我们用cnt【i】(代表前缀和%k == i的数量)来进行维护

#include 
#include 
#include 

using namespace std;

const int N = 100010;
long long  a[N], s[N], cnt[N];
long long  n, k, fins;
int main()
{
    cin >> n >> k;
    for (int i = 1; i <= n; i ++ ) 
    {
        cin >> a[i];
        s[i] = s[i - 1] + a[i];
    }
    cnt[0] = 1;         //余数为0的数初始化为1 因为0 % k 是0
    for (int i = 1; i <= n; i++) {
        fins += cnt[s[i] % k];
        cnt[s[i] % k] ++;       //将当前的区间的余数放进cnt中
    }
    cout << fins;
}

LeetCode523. 连续的子数组和

给你一个整数数组 nums 和一个整数 k ,编写一个函数来判断该数组是否含有同时满足下述条件的连续子数组:

子数组大小 至少为 2 ,且
子数组元素总和为 k 的倍数。
如果存在,返回 true ;否则,返回 false 。

如果存在一个整数 n ,令整数 x 符合 x = n * k ,则称 x 是 k 的一个倍数。0 始终视为 k 的一个倍数。

示例 1:

输入:nums = [23,2,4,6,7], k = 6
输出:true
解释:[2,4] 是一个大小为 2 的子数组,并且和为 6 。
示例 2:

输入:nums = [23,2,6,4,7], k = 6
输出:true
解释:[23, 2, 6, 4, 7] 是大小为 5 的子数组,并且和为 42 。
42 是 6 的倍数,因为 42 = 7 * 6 且 7 是一个整数。
示例 3:

输入:nums = [23,2,6,4,7], k = 13
输出:false

提示:

1 <= nums.length <= 105
0 <= nums[i] <= 109
0 <= sum(nums[i]) <= 231 - 1
1 <= k <= 231 - 1

本题目的思路和上一题目基本类似,我们直接调用umap 参数分别是value,最左边出现的下标

仍然是将前缀和 % k放进map中,将前缀和 % k相同的前缀和相减得到的区间就是k的倍数,然后判断一下

下标的距离是否是>=2的就行了

class Solution {
public:
    bool checkSubarraySum(vector& nums, int k) {
        int n = nums.size();
        if (n < 2) return false;
        unordered_mapmp;
        mp[0] = 0;
        int value = 0;
        for (int i = 0; i < n; i++) {
            value = (value + nums[i]) % k;
            //老样子,计算同时模k相同的区间差就是k倍数的
            if (mp.count(value)) {
                int pre = mp[value];
                if (i - pre >= 2) {
                    return true;
                }
                //不能构成也不更新,保证距离最长
            }else {
                mp[value] = i;
            }
        }
        return false;
    }
};

激光炸d

地图上有 NN 个目标,用整数 Xi,YiXi,Yi 表示目标在地图上的位置,每个目标都有一个价值 WiWi。

注意:不同目标可能在同一位置。

现在有一种新型的激光炸d,可以摧毁一个包含 R×RR×R 个位置的正方形内的所有目标。

激光炸d的投放是通过卫星定位的,但其有一个缺点,就是其爆炸范围,即那个正方形的边必须和 x,yx,y 轴平行。

求一颗炸d最多能炸掉地图上总价值为多少的目标。

输入格式

第一行输入正整数 NN 和 RR,分别代表地图上的目标数目和正方形的边长,数据用空格隔开。

接下来 NN 行,每行输入一组数据,每组数据包括三个整数 Xi,Yi,WiXi,Yi,Wi,分别代表目标的 xx 坐标,yy 坐标和价值,数据用空格隔开。

输出格式

输出一个正整数,代表一颗炸d最多能炸掉地图上目标的总价值数目。

数据范围

0≤R≤1090≤R≤109
0 0≤Xi,Yi≤50000≤Xi,Yi≤5000
0≤Wi≤10000≤Wi≤1000

输入样例:
2 1
0 0 1
1 1 1
输出样例:
1

目标是在网状正方形的边上,区域的话是可以随便移动的,因此每个区域都可以炸掉(R+1)*(R+1)个点

因为边长是R网上就有R+1个点,因此我们枚举区间,判断一下区间中最大值

Y总代码

#include 
#include 
#include 

using namespace std;

const int N = 5010;

int n, m;
int s[N][N];

int main()
{
    int cnt, R;
    cin >> cnt >> R;
    R = min(5001, R);

    n = m = R;
    while (cnt -- )
    {
        int x, y, w;
        cin >> x >> y >> w;
        x ++, y ++ ;
        n = max(n, x), m = max(m, y);
        s[x][y] += w;
    }

    // 预处理前缀和
    for (int i = 1; i <= n; i ++ )
        for (int j = 1; j <= m; j ++ )
            s[i][j] += s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1];

    int res = 0;

    // 枚举所有边长是R的矩形,枚举(i, j)为右下角
    for (int i = R; i <= n; i ++ )
        for (int j = R; j <= m; j ++ )
            res = max(res, s[i][j] - s[i - R][j] - s[i][j - R] + s[i - R][j - R]);

    cout << res << endl;

    return 0;
}


最后,希望大家蓝桥杯取得好成绩!

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5698586.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存