首页 > 其他分享 >一维差分和等差数列差分

一维差分和等差数列差分

时间:2024-09-24 14:50:25浏览次数:9  
标签:arr 数组 int 差分 item vector 一维 include 等差数列

一维差分

  • 不支持边操作边查询

对于数组 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

相关文章

  • 树上差分+lca 黑暗的锁链
    //**太久不写了,感觉很难受。。。比赛最近打得也不好,课内任务又重,还要忙着做项目。何去何从。今天又写了一题,用了树上差分的知识。下面来整理整理。1.首先让我们学一下lca(最小公共父节点) 我用的是倍增来求的。总共其实就是两步:dfs打ST表预处理每个点的上面节点 lca求两......
  • 【数据结构-差分】【hard】力扣995. K 连续位的最小翻转次数
    给定一个二进制数组nums和一个整数k。k位翻转就是从nums中选择一个长度为k的子数组,同时把子数组中的每一个0都改成1,把子数组中的每一个1都改成0。返回数组中不存在0所需的最小k位翻转次数。如果不可能,则返回-1。子数组是数组的连续部分。示......
  • 【C++ 差分数组 前后缀分解】P7404家庭菜园
    本文涉及知识点C++差分数组C++前后缀分解P7404家庭菜园出自洛谷,我简述一下。已知数组a,长度为n(1<=n<=2e5),1<=a[i]<=1e9。一次操作如下:将a[i…j]全+1。问最少操作多少次,使得a成为山形数组,即存在k,a[0…k]严格递增,a[k…]严格递减。前后缀分解+差分数组(错误解法)n=a.......
  • (27)时钟专题--->(027)差分时钟转单端时钟(VHDL)
    1.1.1本节目录1)本节目录2)本节引言3)FPGA简介4)差分时钟转单端时钟(VHDL)5)结束语1.1.2本节引言“不积跬步,无以至千里;不积小流,无以成江海。就是说:不积累一步半步的行程,就没有办法达到千里之远;不积累细小的流水,就没有办法汇成江河大海。1.1.3FPGA简介FPGA(FieldProgrammab......
  • Vue实战指南:Vue中将一维对象数组转换为二维对象数组
    Vue实战指南:Vue中将一维对象数组转换为二维对象数组引言一维对象数组与二维对象数组的概念一维对象数组二维对象数组Vue中转换的方法示例一:使用计算属性实现转换示例二:使用methods中的函数实现转换示例三:使用Vue自定义指令实现转换示例四:使用Vuex进行状态管理实际开发......
  • LeetCode 2848. 与车相交的点(差分法、前缀和)
    题目:2848.与车相交的点思路:差分+前缀和。先找到数组中的最大值N,然后构建一个长度为N+2的数组sta。接着遍历数组,进行差分。最后求前缀和得到每个点的值,然后判断是否大于0即可。时间复杂度0(n)。classSolution{public:intnumberOfPoints(vector<vector<int>>&nu......
  • 【使用 3D FDTD 代码和 UPML 进行微带分支线耦合器分析】三维有限差分时域方法在平面
       ......
  • 【使用UPML的3D FDTD代码进行微带低通滤波器分析】应用三维有限差分时域法分析平面微
       ......
  • Matlab 一维层状声子晶体振动传输特性
        一维声子晶体的传递矩阵法是一种用于研究声波在一维周期性结构中传播的方法。这种方法基于‌波动方程和周期性边界条件,通过计算声波在不同介质中的传播特性,进而分析声子晶体的带隙结构。传递矩阵法可以有效地预测声波在一维声子晶体中的传播行为,包括透射和反射系数等......
  • Java基础 | 数组 | 一维数组 | 二维数组 | 数组初始化 | 索引
    目录一维数组什么是数组数组定义格式格式一格式二数组初始化数组动态初始化什么是动态初始化动态初始化格式动态初始化格式详解等号左边等号右边数组静态初始化什么是静态初始化静态初始化格式完整版格式简化版格式示例代码数组元素访问什么是索引访问数......