一维差分
- 不支持边操作边查询
对于数组 a,定义其差分数组(difference array)为
i = 0 时,d[i] = a[0];
i > 0 时,d[i] = a[i] - a[i-1];
-
性质 1:从左到右累加 d 中的元素,可以得到数组 a。
-
性质 2:如下两个操作是等价的。
- 区间操作:把 a 的子数组 a[i, j] 都加上 x。
- 单点操作:把 d[i] 增加 x,把 d[j+1] 减少 x。特别地,如果 j+1=n,则只需把 d[i] 增加 x。(n 为数组 a 的长度。)
利用性质 2,我们只需要 O(1) 的时间就可以完成数组 a 上的区间操作。最后利用性质 1 从差分数组复原出数组 a。
模板
#include <iostream>
#include <vector>
using namespace std;
/*
1.题目描述:给一个数组 a,经过 m 次修改,输出数组 a
2.输入:
第一行有 n,m,表示数组 a 有 n 个元素,经过 m 次修改
第二行 n 个数,表示 n 个元素
之后 m 行,每行有三个数:l,r,s 表示数组下标从 l 到 r,分别加 s
3.输出:修改后的数组a
4.样例输入:
5 3
1 2 3 4 5
0 2 1
0 4 1
2 3 2
5.样例输出:3 4 7 7 6
6.数据范围:1<=n,m<1e5;
*/
int main() {
int n, m;
cin >> n >> m;
vector<int> arr(n);
for (int i = 0; i < n; ++i)
cin >> arr[i];
// 差分数组
vector<int> d(n);
d[0] = arr[0];
for (int i = 1; i < n; ++i)
d[i] = arr[i] - arr[i - 1];
for (int i = 0, l, r, s; i < m; ++i) {
cin >> l >> r >> s;
d[l] += s;
if (r + 1 < n) d[r + 1] -= s;
}
for (int i = 1; i < n; ++i) {
d[i] += d[i - 1];
}
swap(arr, d);
for (int i = 0; i < n; ++i)
cout << arr[i] << " ";
}
1109. 航班预订统计
#include <iostream>
#include <vector>
using namespace std;
class Solution {
public:
// 先用一个初始值全为 0 的数组 d 计算好每个位置最终要加上的数字,在逐个加到原始数组 arr 上
vector<int> corpFlightBookings(vector<vector<int>> &bookings, int n) {
// 这道题原始数组一开始就是全 0
vector<int> arr(n, 0);
vector<int> d(n, 0);
for (const auto &item: bookings) {
int start = item[0] - 1;
int end = item[1] - 1;
int seats = item[2];
// [start, end] +seats
d[start] += seats;
// [end + 1, 结尾] -seats
if (end + 1 < n) d[end + 1] -= seats;
}
// 算前缀和
for (int i = 1; i < n; ++i)
d[i] += d[i - 1];
// 加到原始数组上
for (int i = 0; i < n; ++i)
arr[i] += d[i];
return arr;
}
};
#include <iostream>
#include <vector>
using namespace std;
class Solution {
public:
// 根据 arr 计算差分数组 d
vector<int> corpFlightBookings(vector<vector<int>> &bookings, int n) {
// 这道题原始数组一开始就是全 0
vector<int> arr(n, 0);
vector<int> d(n);
// 计算差分数组
d[0] = arr[0];
for (int i = 1; i < n; ++i)
d[i] = arr[i] - arr[i - 1];
// 对差分数组操作
for (const auto &item: bookings) {
int start = item[0] - 1;
int end = item[1] - 1;
int seats = item[2];
// [start, end] +seats
d[start] += seats;
// [end + 1, 结尾] -seats
if (end + 1 < n) d[end + 1] -= seats;
}
// 从左往右计算前缀和就能得到最终结果,不用再累加到 arr 上
for (int i = 1; i < n; ++i)
d[i] += d[i - 1];
return d;
}
};
#include <iostream>
#include <vector>
using namespace std;
class Solution {
public:
// 简洁版
vector<int> corpFlightBookings(vector<vector<int>> &bookings, int n) {
// 加两个位置,省去对下标从 1 开始的讨论,省去 +1 越界的讨论
vector<int> d(n + 2, 0);
for (const auto &item: bookings) {
d[item[0]] += item[2];
d[item[1] + 1] -= item[2];
}
// 计算前缀和
for (int i = 1; i <= n; ++i)
d[i] += d[i - 1];
return vector<int>(begin(d) + 1, end(d) - 1);
}
};
等差数列差分
模板
初始 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
---|---|---|---|---|---|---|---|---|---|
l | r | ||||||||
set后 | 0 | s | d-s | 0 | 0 | 0 | -d-e | e | 0 |
第一次算前缀和后 | 0 | s | d | d | d | d | -e | 0 | 0 |
第二次算前缀和后 | 0 | s | s+d | s+2d | s+3d | s+4d | s+5d-e = 0 | 0 | 0 |
/*
一开始 1~n 范围上的数字都是 0。接下来一共有 m 个操作。
每次操作:l~r 范围上依次加上首项 s、末项 e、公差 d 的数列
最终 1~n 范围上的每个数字都要正确得到
*/
// l~r 范围上依次加上首项 s、末项 e、公差 d 的数列
void set(vector<int> arr, int l, int r, int s, int e, int d) {
arr[l] += s;
arr[l + 1] += d - s;
arr[r + 1] -= d + e;
arr[r + 2] += e;
}
// 算两次前缀和
void build(int n, vector<int> arr) {
for (int i = 1; i <= n; i++)
arr[i] += arr[i - 1];
for (int i = 1; i <= n; i++)
arr[i] += arr[i - 1];
}
P4231 三步必杀
#include <iostream>
#include <vector>
using namespace std;
void set(int n, vector<long> &arr, int l, int r, int s, int e, int d) {
arr[l] += s;
arr[l + 1] += d - s;
arr[r + 1] -= d + e;
arr[r + 2] += e;
}
// 算两次前缀和
void build(int n, vector<long> &arr) {
for (int i = 1; i <= n; i++)
arr[i] += arr[i - 1];
for (int i = 1; i <= n; i++)
arr[i] += arr[i - 1];
}
int main() {
int n, m;
scanf("%d %d", &n, &m);
// 开头加一个位置,末尾加两个位置,省去边界讨论
vector<long> arr(n + 3, 0);
for (int i = 0, l, r, s, e, d; i < m; ++i) {
scanf("%d %d %d %d", &l, &r, &s, &e);
d = (e - s) / (r - l);
set(n, arr, l, r, s, e, d);
}
build(n, arr);
long MAX = arr[0], xorSum = 0;
for (int i = 1; i <= n; ++i) {
if (arr[i] > MAX) MAX = arr[i];
xorSum ^= arr[i];
}
cout << xorSum << " " << MAX;
}
P5026 Lycanthropy
#include <iostream>
using namespace std;
// 湖泊最大宽度
const int MAXN = 1000001;
// 左侧影响最远的位置到达了x - 3 * v + 1
// 右侧影响最远的位置到达了x + 3 * v - 1
const int OFFSET = 30001;
int arr[OFFSET + MAXN + OFFSET];
int n, m;
// 整体右移 OFFSET
void set(int l, int r, int s, int e, int d) {
arr[l + OFFSET] += s;
arr[l + 1 + OFFSET] += d - s;
arr[r + 1 + OFFSET] -= d + e;
arr[r + 2 + OFFSET] += e;
}
// 一个人落水会有四段等差数列
void fall(int v, int x) {
set(x - 3 * v + 1, x - 2 * v, 1, v, 1);
set(x - 2 * v + 1, x, v - 1, -v, -1);
set(x + 1, x + 2 * v, -v + 1, v, 1);
set(x + 2 * v + 1, x + 3 * v - 1, v - 1, 1, -1);
}
void build() {
for (int i = 1; i <= m + OFFSET; i++) {
arr[i] += arr[i - 1];
}
for (int i = 1; i <= m + OFFSET; i++) {
arr[i] += arr[i - 1];
}
}
int main() {
while (cin >> n >> m) {
for (int i = 0, v, x; i < n; i++) {
cin >> v >> x;
fall(v, x);
}
build();
int start = OFFSET + 1;
cout << arr[start++];
for (int i = 2; i <= m; i++) {
cout << " " << arr[start++];
}
cout << endl;
}
return 0;
}
标签:arr,数组,int,差分,item,vector,一维,include,等差数列
From: https://www.cnblogs.com/sprinining/p/18429136