一维差值维护心得

一维差值维护是一种简单的小算法,该算法用一个巧妙地数列机制解决了多次对数列进行数据加减操作的复杂度,这个算法的思维偏向于动态规范。下面我们从一个问题开始入手介绍这个算法:

问题描述: 
已知n个数的数列a,有m次操作,每次操作给定l,r,k三个数,使得al到ar内所有数加上k。注意l到r的区间包含al和ar两个数。 
输入数据: 
第一行n和m,接下来一行有n个数,表示数列a,接下来有m行,每行有三个数l,r,k,详细解释参考问题描述。 
输出数据: 
1行n个数,表示经过m次操作之后的数组a。 
输入样例: 
5 2 
1 2 3 4 5 
2 4 3 
3 5 2 
输出样例: 
1 5 8 9 7 
数据范围: 
0<N,M≤100000 
ai是int范围内的任意整数,输入数据保证ai+k在int的范围内

对于这个问题而言,我们一开始的思路很简单,就是暴力模拟,每次操作遍历,数组的l到r区间,每个都加上k最后直接遍历数组输出。这样的算法复杂度是O(mn)的,对于这个数据范围肯定会超时。

所以我们是否可以发现一种方式,用这种方式的条件下我们对于某个区间的数据,只需要做两个操作就可以把一个区间都加上某个数。试着去推导这个东西

,我们会去想,什么东西可以在我们做区间加上某一个数这个操作的时候保持不变呢?

于是我们想到了对于数列中的某两个数,这两个数之间的差是不变的。而且当这两个数同时增加k的时候,两数的差仍然不变。因此我们只要求出对于一个数组的所有两个数之间的差,我是否就可以直接在这个差值里面做某个操作,让我的整个数组发生变化。

这就是差值维护,差值维护就是对某一个数列每两两数之间求出差值,这个差值组成一个新数列,设这个数列为S,我对Si加上一个k,则表示ai往后的所有数都加上了一个k,试想一下这个过程。对于一个差值数列,修改某一个位置i的差值,增加一个k,在我把差值数组重新生成原数组时,由于i位置的差值加了k,所以生成的原值也就加了k,而之后的所有数的生成依赖前面的数字,这就使得整个数列从i开始到结束都增加了k。

这里说明一下差值维护的过程,设差值数组为S,原数组为a,则满足S1=a1且Si=ai−ai−1,这样就把S数组维护出来了,之后如果我们想要取得原数组,只需要按照a1=S1,ai=Si+ai−1这个公式即可。那么我们再考虑,对于S数组,我们考虑Si+=k表示的是什么意思。当我执行这个操作的时候,我再生成原数组,会发现ai的值实际上相比操作之前ai的值多了k,而我们是递推得到原数组的,也就是说ai+1的计算需要ai为基础,因为ai增大了k,所以ai+1也同样的增大了k。所以我们做Si+=k这个操作的目的实际上就是把从i到数列结束的所有数都加上了一个k。思考一下这个过程,你会体会到它的巧妙。

所以我们现在来考虑最后一个问题,如何使得从l开始到r的所有数都加上k。根据之前的结论,这个要求就显得很简单了,我们不妨让sl加上k,这表示从l开始到结束的所有数加上k,再让sr+1减去k,表示从r+1到结束的所有数都减去k,由于之前加k现在减k,所以实际上r+1到结束的所有数都没有变化,而l到r的所有数只加了k,满足我们的要求,因此这个算法成立。

根据这个算法就可以效率更高的计算出刚刚问题的解,这个算法的时间复杂度为O(m+n)。

以上就是关于一维差值维护算法的介绍。

 

include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <cmath>
#include <ctime>
#include <algorithm>
using namespace std;

const int MAXN=100010;

int n,m,a[MAXN],s[MAXN];

//读入优化
int read(){
    char ch=getchar();
    bool fl=0;
    int r=0;
    if(ch==‘-‘){
        fl=1;
        ch=getchar();
    }
    while(ch>=‘0‘&&ch<=‘9‘){
        r*=10;
        r+=ch-‘0‘;
        ch=getchar();
    }
    return fl?-r:r;
}

//输出优化
void write(int x){
    if(x<0){
        putchar(‘-‘);
        x=-x;
    }
    if(x>9){
        write(x/10);
    }
    putchar(x%10+‘0‘);
}

int main(){
    freopen("in.txt","r",stdin);
    freopen("out.txt","w",stdout);
    n=read();
    m=read();
    a[0]=read();
    s[0]=a[0];
    for(int i=1;i<n;++i){
        a[i]=read();
        s[i]=a[i]-a[i-1];//O(n)的生成差值数组
    }
    for(int i=0;i<m;++i){
        int l,r,k;
        l=read();
        r=read();
        k=read();
        --l;
        s[l]+=k;//O(2)的操作,所有操作一共O(m)
        s[r]-=k;
    }
    write(s[0]);
    putchar(‘ ‘);
    for(int i=1;i<n;++i){
        s[i]+=s[i-1];
        write(s[i]);
        putchar(‘ ‘);//输出接着O(n)
    }
    //总复杂度O(m+n)
    return 0;
}

  

原文地址:https://www.cnblogs.com/Roni-i/p/8157351.html

时间: 2024-10-08 22:14:00

一维差值维护心得的相关文章

动态集合中两个最接近的数的差值

题目: 思考: 既然是动态集合,那么我们需要用链表来存储数据方便插入和删除.于是我们可以选用二叉链表,也就是红黑树来存储数据,红黑树由于比较平衡,所以可以得到比较好的查询时间.但是我们并不是直接拿红黑树就可以用了,因为基本的红黑树没有MIN_GAP操作,所以需要自己修改和维护原始的红黑树. 如何给红黑树添加MIN_GAP操作呢?我们需要先给红黑树结构中加入MAX,MIN指针.这个操作可以参考最坏时间为O(1)的求最大小值,当然只取了里面部分新增代码.既然新指针已经添加好了,那么我们以原有红黑树插

阿里 2014-08-29 校招机试题 求一个存放整数的二叉树相差最大的两节点差值绝对值

题目:写一个函数,输入一个二叉树,树中每个节点存放了一个整数值,函数返回这颗二叉树中相差最大的两个节点间的差值绝对值.请注意程序效率. 如果是数值之差,感觉怎么着也得遍历一遍,直接修改下二叉树的基本遍历代码就可以. #include<stdio.h> #include <stdlib.h> typedef struct Node { int data; struct Node * left; struct Node * right; } BitNode, *BiTree; /* 求

查找(哨兵查找、二分查找、差值查找)

#include <iostream> using namespace std; #define N 10 int fib(int n) { if(n == 0) { return 0; } else if(n == 1) { return 1; } else { return (fib(n-1) + fib(n-2)); } } //普通查找: int sequenctial_Search(int *a,int n,int key) { int i; a[0] = key; i = n; w

Houdini中给火花渲染准确的运动模糊 - 给运动模糊做非线性差值的方法以及固定粒子点数的方法

估计大家都知道使用运动速度来进行运动模糊的渲染,但是往往这个方法得到的运动模糊都是线性变化的,虽然乍一看没什么问题,但是如果想要每一帧的模糊轨迹也是有曲线变化的而不是僵硬的直来直去的话,使用trail算个速度来做的运动模糊是永远做不到这一点的. 这里我想通过常用的火花(spark)的运动模糊来讲一讲我所了解的一些比较好的方法. 所谓渲染中的运动模糊无非就是差值算法.目前使用的比较多的主要有两种.第一种就是上面说到的直接使用速度来线性差值,这种方法会计算每一个点的速度方向,计算出前一帧或者后一帧的

华为机试—差值排序

对整形数组按照和指定整数的差值大小进行排序,按照差值升序排列返回. 要求实现方法: public staticint[] calcTimes(int[] num, int value); [输入] num:整型数组: value 指定的整数 [返回] 按照升序返回整型数组,排序按照各个整数和指定整数的差值大小 [注意]只需要完成该函数功能算法,中间不需要有任何IO的输入输出 示例 输入:num = {1,2,3,4,5,10,17,18,19}  value = 5 返回:{5,4,3,2,1,

二分查找的改进--差值查找

差值查找 在二分查找中,我们每次比较都可以排除一半的数据量,这个已经是很高效了.如果利用关键字本身的信息,每次排除的数据量充分依赖于关键字的大小,则查找会更高效,这就是差值查找的思想. 下面通过示例代码,比较二分查找和差值查找的不同,在不同中领略差值查找的改良之处. #include <stdio.h> #include <stdlib.h> int InterSearch(int *array, int n, int key) { if (array && n &

hdu1598find the most comfortable road(并查集+枚举,求起点到终点的边中最大边减最小边差值最小)

Problem Description XX星有许多城市,城市之间通过一种奇怪的高速公路SARS(Super Air Roam Structure---超级空中漫游结构)进行交流,每条SARS都对行驶在上面的Flycar限制了固定的Speed,同时XX星人对 Flycar的"舒适度"有特殊要求,即乘坐过程中最高速度与最低速度的差越小乘坐越舒服 ,(理解为SARS的限速要求,flycar必须瞬间提速/降速,痛苦呀 ), 但XX星人对时间却没那么多要求.要你找出一条城市间的最舒适的路径.(

差值的再议-Hermite差值

1. 插值法 插值法又称"内插法",是利用函数f (x)在某区间中已知的若干点的函数值,作出适当的特定函数,在区间的其他点上用这特定函数的值作为函数f (x)的近似值,这种方法称为插值法. 如果这特定函数是多项式,就称它为插值多项式. 2. 经典的Hermite差值 Hermite插值是利用未知函数f(x)在插值节点上的函数值及导数值来构造插值多项式的.因此,Hermite插值满足在节点上等于给定函数值,而且在节点上的导数值也等于给定的导数值. 对于高阶导数的情况,Hermite插值多

基于Qt的OpenGL可编程管线学习(17)- 差值、反差值、排除

1.差值 shader //差值 uniform sampler2D U_MainTexture; uniform sampler2D U_SubTexture; varying vec2 M_coord; void main() {         vec4 blendColor = texture2D(U_SubTexture, M_coord);         vec4 baseColor = texture2D(U_MainTexture, M_coord);         gl_F