浅谈差分数组的原理及简单应用

一、差分数组的定义及用途

1.定义:

对于已知有n个元素的离线数列d,我们可以建立记录它每项与前一项差值的差分数组f:显然,f[1]=d[1]-0=d[1];对于整数i∈[2,n],我们让f[i]=d[i]-d[i-1]。

2.简单性质:

(1)计算数列各项的值:观察d[2]=f[1]+f[2]=d[1]+d[2]-d[1]=d[2]可知,数列第i项的值是可以用差分数组的前i项的和计算的,即d[i]=f[i]的前缀和。

(2)计算数列每一项的前缀和:第i项的前缀和即为数列前i项的和,那么推导可知

即可用差分数组求出数列前缀和;

3.用途:

(1)快速处理区间加减操作:

假如现在对数列中区间[L,R]上的数加上x,我们通过性质(1)知道,第一个受影响的差分数组中的元素为f[L],即令f[L]+=x,那么后面数列元素在计算过程中都会加上x;最后一个受影响的差分数组中的元素为f[R],所以令f[R+1]-=x,即可保证不会影响到R以后数列元素的计算。这样我们不必对区间内每一个数进行处理,只需处理两个差分后的数即可;

(2)询问区间和问题:

由性质(2)我们可以计算出数列各项的前缀和数组sum各项的值;

那么显然,区间[L,R]的和即为ans=sum[R]-sum[L-1];

二、相关题目

1.[HDU1556]Color the ball

Description

N个气球排成一排,从左到右依次编号为1,2,3....N.每次给定2个整数a b(a <= b),lele便为骑上他的“小飞鸽"牌电动车从气球a开始到气球b依次给每个气球涂一次颜色。但是N次以后lele已经忘记了第I个气球已经涂过几次颜色了,你能帮他算出每个气球被涂过几次颜色吗?

Input:每个测试实例第一行为一个整数N,(N <= 100000).接下来的N行,每行包括2个整数a b(1 <= a <= b <= N)。当N = 0,输入结束。

Output:每个测试实例输出一行,包括N个整数,第I个数代表第I个气球总共被涂色的次数。

Solution

1.记录各次操作,对差分数组进行对应修改,改变量为1(用途1);

2.使用性质(1)计算各项的值即可;

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
int d[100010],a[100010],l,r;
int main(){
    int n;
    while(scanf("%d",&n),n)
    {
        memset(d,0,sizeof(d));
        memset(a,0,sizeof(a));
        for(int i=1;i<=n;++i){
            scanf("%d%d",&l,&r);
            d[l]+=1;
            d[r+1]-=1;
        }
        for(int i=1;i<=n;++i) a[i]=a[i-1]+d[i];
        for(int i=1;i<n;++i) printf("%d ",a[i]);
        printf("%d\n",a[n]);
    }
    return 0;
}

2.[NKOJ3754]数列游戏

Description

给定一个长度为N的序列,首先进行A次操作,每次操作在Li和Ri这个区间加上一个数Ci。

然后有B次询问,每次询问Li到Ri的区间和。

初始序列都为0。

输入格式:

第一行三个整数N A B。(1<=N<=1000000,1<=A<=N,A<=B<=N)

接下来A行,每行三个数Li Ri Ci。(1<=Li<=N,Li<=Ri<=N,|Ci|<=100000000000000)。

接下来B行,每行两个数 Li Ri。范围同上。

输出格式:

对于每次询问,输出一行一个整数。

因为最后的结果可能很大,请对结果mod 1000000007。

Solution

1.应用(1)处理区间加;

2.用性质(1)求出修改后数列,再求出相应数列和(应用2)或直接用性质(2)求解;

3.注意随时取模;

#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<cstdio>
const long long mod=1000000007;
using namespace std;
long long d[100010],f[100010],sum[100010];
int main(){
    int n,a,b;
    scanf("%d%d%d",&n,&a,&b);
    memset(d,0,sizeof(d));
    memset(f,0,sizeof(f));
    memset(sum,0,sizeof(sum));
    for(int i=1;i<=a;++i){
        long long l,r,c;
        scanf("%ld%ld%ld",&l,&r,&c);
        f[l]=(f[l]+c)%mod;
        f[r+1]=(f[r+1]-c)%mod;
    }
    for(int i=1;i<=n;++i)  d[i]=(d[i-1]+f[i])%mod;
    for(int i=1;i<=n;i++) sum[i]=(sum[i-1]+d[i])%mod;
    for(int i=1;i<=b;++i){
        int l,r;
        scanf("%d%d",&l,&r);
        printf("%ld\n",(sum[r]-sum[l-1])%mod);
        //printf("%ld\n",temp>=0?temp:temp+mod);//防止结果为负;
    }
    return 0;
} 

原文地址:https://www.cnblogs.com/COLIN-LIGHTNING/p/8436624.html

时间: 2024-08-29 21:48:32

浅谈差分数组的原理及简单应用的相关文章

浅谈差分数组的应用(二)&amp;[NOIP2012]借教室题解

[NOIP2012提高&洛谷P1083]借教室 Description 在大学期间,经常需要租借教室.大到院系举办活动,小到学习小组自习讨论,都需要向学校申请借教室.教室的大小功能不同,借教室人的身份不同,借教室的手续也不一样.面对海量租借教室的信息,我们自然希望编程解决这个问题. 我们需要处理接下来n天的借教室信息,其中第i天学校有ri个教室可供租借.共有m份订单,每份订单用三个正整数描述,分别为dj,sj,tj,表示某租借者需要从第sj天到第tj天租借教室(包括第sj天和第tj天),每天需要

浅谈差分约束系统——图论不等式的变形

浅谈差分约束系统——图论不等式的变形 ----yangyaojia 版权声明:本篇随笔版权归作者YJSheep(www.cnblogs.com/yangyaojia)所有,转载请保留原地址! 一.定义 如若一个系统由n个变量和m个不等式组成,并且这m个不等式对应的系数矩阵中每一行有且仅有一个1和-1,其它的都为0,这样的系统称为差分约束( difference constraints )系统. 二.分析 简单来说就是给你n个变量,给m个形如x[i]-x[j]≥k①或x[i]-x[j]≤k②.求两

浅谈动态数组原理及其实现

stl中的vector是竞赛中常用的容器,原因在于省内存,O(1)在后端插入和删除.随机下标访问,今天就来谈谈它的实现. 最简单的一个动态数组 动态数组并不是真正意义上的动态的内存,而是一块连续的内存,当添加新的元素时,容量已经等于当前的大小的时候(存不下了),执行下面3步 重新开辟一块大小为当前容量两倍的数组 把原数据拷贝过去 释放掉旧的数组 完事后再把这个元素加在后面. 那么你一定会很好奇,它为什么会是O(1).这个是均摊下来的结果,我们只需要证明总共拷贝的元素个数是O(n)级别的就好了(因

TODO:浅谈pm2基本工作原理

要谈Node.js pm2的工作原理,需要先来了解撒旦(Satan)和上帝(God)的关系. 撒旦(Satan),主要指<圣经>中的堕天使(也称堕天使撒旦),他是反叛上帝耶和华的堕天使(Fallen Angels),曾经是上帝座前的天使,后来他因骄傲自大妄想与神同等而堕落成为魔鬼,被看作与上帝的力量相对的邪恶.黑暗之源. 简单的说Satan是破坏神,就是进程的异常退出.kill等:God是守护神,保护进程.重启进程等. 一图胜千言,pm2的 RPC基本框架.Client与Daemon是采用了R

JAVA NIO之浅谈内存映射文件原理与DirectMemory

Java类库中的NIO包相对于IO 包来说有一个新功能是内存映射文件,日常编程中并不是经常用到,但是在处理大文件时是比较理想的提高效率的手段.本文我主要想结合操作系统中(OS)相关方面的知识介绍一下原理. 在传统的文件IO操作中,我们都是调用操作系统提供的底层标准IO系统调用函数  read().write() ,此时调用此函数的进程(在JAVA中即java进程)由当前的用户态切换到内核态,然后OS的内核代码负责将相应的文件数据读取到内核的IO缓冲区,然后再把数据从内核IO缓冲区拷贝到进程的私有

[转自SA]浅谈nginx的工作原理和使用

nginx apache 简单对比 nginx 相对 apache 的优点: 轻量级,同样起web 服务,比apache 占用更少的内存及资源 抗并发,nginx 处理请求是异步非阻塞的,而 apache 则是阻塞型的,在高并发下 nginx 能保持低资源低消耗高性能 高度模块化的设计,编写模块相对简单 社区活跃 配置简洁 apache 相对nginx 的优点: rewrite ,比 nginx 的 rewrite 强大 模块超多 少 bug ,nginx 的 bug 相对较多 超稳定 配置复杂

浅谈JavaScript DDOS 攻击原理与防御

前言 DDoS(又名"分布式拒绝服务")攻击历史由来已久,但却被黑客广泛应用.我们可以这样定义典型的DDoS攻击:攻击者指使大量主机向服务器发送数据,直到超出处理能力进而无暇处理正常用户的合法请求,最终导致用户无法正常访问网站. 近年来,DDoS攻击手段已日趋多元化——攻击者通过各种奇技淫巧诱使不知情主机参加攻击.比如,[注1]历史上数据量最大(超过400Gbps)的DDoS攻击就是通过[注2]NTP反射完成的.时至今日,我们已经发现一个令人不安的趋势:攻击者通过恶意的JavaScri

浅谈Javascript数组的使用

数组的大小 js数组可以动态调整大小,更确切点说,它没有数组越界的概念,a[a.length]没什么问题.比如声明一个数组a = [1, 3, 5],现在的数组大小是3,最后一个元素的索引是2,但是你依然可以使用a[3],访问a[3]返回的是undefined,给a[3]赋值:a[3] = 7,是给数组a添加了一个元素,现在数组a的长度是4了.你可以试试把下面这段代码放到浏览器里运行下: var a = []; for(int i = 0; i <= a.length; i++) { a[a.l

浅谈后缀数组

1. 概述 后缀数组是一种解决字符串问题的有力工具.相比于后缀树,它更易于实现且占用内存更少.在实际应用中,后缀数组经常用于解决字符串有关的复杂问题. 本文大部分内容摘自参考资料[1][2]. 2. 后缀数组 2.1   几个概念 (1)后缀数组SA 是一个一维数组,它保存1..n 的某个排列SA[1],SA[2],--,SA[n],并且保证Suffix(SA[i]) < Suffix(SA[i+1]),1≤i<n.也就是将S 的n 个后缀从小到大进行排序之后把排好序的后缀的开头位置顺次放入S