2024蓝桥杯省赛保奖突击班-Day2-前缀和、差分、尺取_笔记_练习题解

3月25日-课堂笔记

  • 前缀和预处理 O ( n ) \mathcal{O}(n) O(n)
cpp 复制代码
s[1] = a[1];
for(int i = 2; i <= n; ++ i)
	s[i] = s[i - 1] + a[i];
  • 利用前缀和查询区间和 O ( 1 ) O(1) O(1)
cpp 复制代码
long long calc(int l, int r) {
	return l == 1 ? s[r] : s[r] - s[l - 1];
}
  • 差分序列的求法
cpp 复制代码
c[1] = a[1];
for(int i = 2; i <= n; ++ i) c[i] = a[i] - a[i - 1];
  • 原序列上区间[l, r]修改相当于差分序列上两个单点修改
cpp 复制代码
c[l] += v;
c[r + 1] -= v;
  • 区间加等差数列对应二次差分序列上常数个单点修改
  • 一般等差数列: ..., 0 , a , a + d , a + 2 d , ... , a + ( k − 1 ) d , 0 , 0 , ... 0, a, a+d, a+2 d, \ldots, a+(k-1) d, 0,0, \ldots 0,a,a+d,a+2d,...,a+(k−1)d,0,0,...
  • 一次差分之后: ..., 0 , a , d , d , ... , d , − a − ( k − 1 ) d , 0 , ... 0, a, d, d, \ldots, d,-a-(k-1) d, 0, \ldots 0,a,d,d,...,d,−a−(k−1)d,0,...
  • 二次差分之后: ..., 0 , a , d − a , 0 , ... , 0 , − a − k d , a + ( k − 1 ) d , ... 0, a, d-a, 0, \ldots, 0,-a-k d, a+(k-1) d, \ldots 0,a,d−a,0,...,0,−a−kd,a+(k−1)d,...
  • 尺取法
cpp 复制代码
for(int l = 1, r = 0; r <= n; ++ l) {
	while(num < m && r < n) ...;
	if(...) break;
	...
}
  • 双栈法维护尺取
    插入/删除 -> 插入/合并

练习题解

B3612 求区间和

题目链接:B3612
参考思路

前缀和模板题。

C++参考代码
cpp 复制代码
#include <iostream>
#include <vector>

using namespace std;

int main() {
    int n, m;
    cin >> n;
    vector<int> a(n), prefix_sum(n + 1, 0);
    
    for (int i = 0; i < n; ++i)
        cin >> a[i];

    for (int i = 1; i <= n; ++i)
        prefix_sum[i] = prefix_sum[i - 1] + a[i - 1];

    cin >> m;
    while (m--) {
        int l, r;
        cin >> l >> r;
        cout << prefix_sum[r] - prefix_sum[l - 1] << endl;
    }

    return 0;
}
Java参考代码
java 复制代码
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        int[] a = new int[n];
        int[] prefixSum = new int[n + 1];

        for (int i = 0; i < n; i++)
            a[i] = scanner.nextInt();

        for (int i = 1; i <= n; i++)
            prefixSum[i] = prefixSum[i - 1] + a[i - 1];

        int m = scanner.nextInt();
        while (m-- > 0) {
            int l = scanner.nextInt();
            int r = scanner.nextInt();
            System.out.println(prefixSum[r] - prefixSum[l - 1]);
        }
    }
}
Python参考代码
py 复制代码
n = int(input())
a = list(map(int, input().split()))

prefix_sum = [0] * (n + 1)
for i in range(1, n + 1):
    prefix_sum[i] = prefix_sum[i - 1] + a[i - 1]

m = int(input())
for _ in range(m):
    l, r = map(int, input().split())
    print(prefix_sum[r] - prefix_sum[l - 1])

P2367 语文成绩

题目链接:P2367
参考思路
C++参考代码
cpp 复制代码
#include <iostream>
#include <vector>

using namespace std;

int main() {
    int n, p;
    cin >> n >> p;
    vector<int> scores(n), diff(n, 0);

    for (int i = 0; i < n; ++i)
        cin >> scores[i];

    while (p--) {
        int x, y, z;
        cin >> x >> y >> z;
        diff[x - 1] += z;
        if(y < n) diff[y] -= z;
    }

    for (int i = 1; i < n; ++i)
        diff[i] += diff[i - 1];

    int min_score = scores[0] + diff[0];
    for (int i = 1; i < n; ++i) {
        scores[i] += diff[i];
        min_score = min(min_score, scores[i]);
    }

    cout << min_score << endl;

    return 0;
}
Java参考代码
java 复制代码
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        int p = scanner.nextInt();
        int[] scores = new int[n];
        int[] diff = new int[n];

        for (int i = 0; i < n; i++)
            scores[i] = scanner.nextInt();
            
        while (p-- > 0) {
            int x = scanner.nextInt();
            int y = scanner.nextInt();
            int z = scanner.nextInt();
            diff[x - 1] += z;
            if (y < n)
                diff[y] -= z;
        }
        int minScore = scores[0] + diff[0];
        for (int i = 1; i < n; i++) {
            diff[i] += diff[i - 1];
            scores[i] += diff[i];
            minScore = Math.min(minScore, scores[i]);
        }

        System.out.println(minScore);
    }
}
Python参考代码
py 复制代码
n, p = map(int, input().split())
scores = list(map(int, input().split()))
diff = [0] * n

for _ in range(p):
    x, y, z = map(int, input().split())
    diff[x - 1] += z
    if y < n:
        diff[y] -= z

min_score = scores[0] + diff[0]
for i in range(1, n):
    diff[i] += diff[i - 1]
    scores[i] += diff[i]
    min_score = min(min_score, scores[i])

print(min_score)

P3406 海底高铁

题目链接:P3406
思路:本题可以使用差分数组和前缀和求出每一段需要经过的次数 再用贪心策略在2种买票方式的最优中选择。
C++参考代码
cpp 复制代码
#include <iostream>
#include <algorithm>

using namespace std;
long long c[100005] = {0};
int main() {
    int n, m;
    cin >> n >> m;
    
    long long sum = 0, ans = 0;
    int p1 = 0, p2 = 0, a, b, c1;

    if (m > 0) {
        cin >> p1;
    }

    for (int i = 2; i <= m; i++) {
        cin >> p2;
        if (p1 < p2) {
            c[p1]++;
            c[p2]--;
        } else {
            c[p2]++;
            c[p1]--;
        }
        p1 = p2;
    }

    for (int i = 1; i < n; i++) {
        sum += c[i];
        cin >> a >> b >> c1;
        if (sum != 0) {
            ans += min(a * sum, b * sum + c1);
        }
    }

    if (m <= 1) {
        ans = 0;
    }

    cout << ans;

    return 0;
}
Java参考代码
java 复制代码
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        int m = scanner.nextInt();
        long[] c = new long[100005];
        long sum = 0, ans = 0;
        int p1 = 0, p2 = 0, a, b, c1;

        if (m > 0) {
            p1 = scanner.nextInt();
        }

        for (int i = 2; i <= m; i++) {
            p2 = scanner.nextInt();
            if (p1 < p2) {
                c[p1]++;
                c[p2]--;
            } else {
                c[p2]++;
                c[p1]--;
            }
            p1 = p2;
        }

        for (int i = 1; i < n; i++) {
            sum += c[i];
            a = scanner.nextInt();
            b = scanner.nextInt();
            c1 = scanner.nextInt();
            if (sum != 0) {
                ans += Math.min(a * sum, b * sum + c1);
            }
        }

        if (m <= 1) {
            ans = 0;
        }

        System.out.println(ans);
        scanner.close();
    }
}
Python参考代码
py 复制代码
n, m = map(int, input().split())
c = [0] * 100005
sum = 0
ans = 0
P = list(map(int,input().split()))
p1 = P[0]
for i in range(1,len(P)):
    p2 = P[i]
    if p1 < p2:
        c[p1] += 1
        c[p2] -= 1
    else:
        c[p2] += 1
        c[p1] -= 1
    p1 = p2

for i in range(1, n):
    sum += c[i]
    a, b, c1 = map(int, input().split())
    if sum != 0:
        ans += min(a * sum, b * sum + c1)

if m <= 1:
    ans = 0

print(ans)

P4552 IncDec Sequence

题目链接:P4552
参考思路:(这是一个比较困难的题)

题中要使所有数都一样。那么,也就是说,在差分的数组 中,除了第一个数字外,其他的数字必须为0

那么我们要做的,就是使除了第一个数字外,其他的数字必须为0。

我们知道差分的公式为 c [ l ] + = v ; c [ r + 1 ] − = v c[l]+=v;c[r+1]-=v c[l]+=v;c[r+1]−=v;

那么我们可以得出结论:
最少次数 就是在差分序列中的正数相加的值负数相加的绝对值 的较大值。

那么,如何解决方法的种数呢?这又是转换法。

思考,差分后的第一个数字的种数是不是就是题目要求的方法数量。

那么要改变差分的第一个数字,是不是以 c [ 1 ] + + , c [ i ] − − 或 c [ 1 ] − − , c [ i ] + + c[1]++,c[i]--或c[1]--,c[i]++ c[1]++,c[i]−−或c[1]−−,c[i]++的方法来改变。

由于要求步数最少,要在差分数组中所有的正数或负数已经和其他数相互抵消完后,才能用 c [ 1 ] c[1] c[1]来勾兑。

那么答案就是正数和负数的绝对值的最大值减去正数和负数的绝对值的最小值。

C++参考代码
cpp 复制代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll n,sum1=0,sum2=0;
int main()
{
	cin >> n;
    vector<ll>a(n+1,0);
	for(int i = 1 ; i <= n ; i++)
	{
		cin >> a[i];
	}
    vector<ll>C(n+1,0);
	for(int i = 2 ; i <= n ; i++)
	{
		C[i] = a[i] - a[i - 1];
		if(C[i] > 0) sum1 += C[i];
		else sum2 -= C[i];
	}
	cout << max(sum1 , sum2) << endl ;
    cout << abs(sum1 - sum2) + 1 << endl ;
    return 0;
}
Java参考代码
java 复制代码
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        long[] a = new long[n + 1];  
        for (int i = 1; i <= n; i++) {
            a[i] = scanner.nextLong();
        }
        
        long[] C = new long[n + 1];  
        long sum1 = 0, sum2 = 0;
        for (int i = 2; i <= n; i++) {
            C[i] = a[i] - a[i - 1];
            if (C[i] > 0) sum1 += C[i];
            else sum2 -= C[i];
        }
        
        System.out.println(Math.max(sum1, sum2));
        System.out.println(Math.abs(sum1 - sum2) + 1);
        scanner.close();
    }
}
Python参考代码
py 复制代码
n = int(input())
a = [0] * (n + 1)  
for i in range(1, n + 1):
    a[i] = int(input())

C = [0] * (n + 1)
sum1 = sum2 = 0
for i in range(2, n + 1):
    C[i] = a[i] - a[i - 1]
    if C[i] > 0:
        sum1 += C[i]
    else:
        sum2 -= C[i]  

print(max(sum1, sum2))
print(abs(sum1 - sum2) + 1)
相关推荐
我要吐泡泡了哦4 分钟前
GAMES104:15 游戏引擎的玩法系统基础-学习笔记
笔记·学习·游戏引擎
骑鱼过海的猫1234 分钟前
【tomcat】tomcat学习笔记
笔记·学习·tomcat
limingade1 小时前
手机实时提取SIM卡打电话的信令和声音-新的篇章(一、可行的方案探讨)
物联网·算法·智能手机·数据分析·信息与通信
贾saisai2 小时前
Xilinx系FPGA学习笔记(九)DDR3学习
笔记·学习·fpga开发
北岛寒沫2 小时前
JavaScript(JS)学习笔记 1(简单介绍 注释和输入输出语句 变量 数据类型 运算符 流程控制 数组)
javascript·笔记·学习
烟雨666_java2 小时前
JDBC笔记
笔记
GEEKVIP2 小时前
Android 恢复挑战和解决方案:如何从 Android 设备恢复删除的文件
android·笔记·安全·macos·智能手机·电脑·笔记本电脑
jiao000014 小时前
数据结构——队列
c语言·数据结构·算法
铁匠匠匠4 小时前
从零开始学数据结构系列之第六章《排序简介》
c语言·数据结构·经验分享·笔记·学习·开源·课程设计
Moliay5 小时前
【资料分析】刷题日记2
笔记·公考·行测·常识·资料分析