一维差分和等差数列差分

一维差分

  • 不支持边操作边查询

对于数组 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;
}
posted @ 2024-09-24 14:56  n1ce2cv  阅读(15)  评论(0编辑  收藏  举报