3月25日-课堂笔记
- 前缀和预处理 O ( n ) \mathcal{O}(n) O(n)
s[1] = a[1];
for(int i = 2; i <= n; ++ i)
s[i] = s[i - 1] + a[i];
- 利用前缀和查询区间和 O ( 1 ) O(1) O(1)
long long calc(int l, int r) {
return l == 1 ? s[r] : s[r] - s[l - 1];
}
- 差分序列的求法
c[1] = a[1];
for(int i = 2; i <= n; ++ i) c[i] = a[i] - a[i - 1];
- 原序列上区间[l, r]修改相当于差分序列上两个单点修改
c[l] += v;
c[r + 1] -= v;
- 区间加等差数列对应二次差分序列上常数个单点修改
- 尺取法
for(int l = 1, r = 0; r <= n; ++ l) {
while(num < m && r < n) ...;
if(...) break;
...
}
- 双栈法维护尺取
插入/删除 -> 插入/合并
练习题解
B3612 求区间和
题目链接:B3612
参考思路
前缀和模板题。
C++参考代码
#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参考代码
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参考代码
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++参考代码
#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参考代码
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参考代码
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++参考代码
#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参考代码
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参考代码
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++参考代码
#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参考代码
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参考代码
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)