TT's Magic Cat(差分模版题)

问题描述

给定数组a,给定操作q个操作: l , r , c l,r,c l,r,c,代表将a数组从 a [ l ] a[l] a[l] a [ r ] a[r] a[r]全部加上c,问q个操作后a数组的结果

Input

第一行输入n和q ( 1 ≤ n , q ≤ 2 × 1 0 5 ) (1\le n,q\le 2\times 10^5) (1n,q2×105),代表数组有n个元素,q个操作
第二行输入数组a,其中 − 1 0 6 ≤ a i ≤ 1 0 6 -10^6\le a_i\le 10^6 106ai106
后面q行,每行输入三个数字 l , r , c ( 1 ≤ l , r ≤ n , − 1 0 5 ≤ c ≤ 1 0 5 ) l,r,c(1\le l,r\le n,-10^5\le c\le 10^5) l,r,c(1l,rn,105c105)

Output

输出a数组,中间用空格隔开

Sample input

4 2
-3 6 8 4
4 4 -2
3 3 1

Sample output

-3 6 9 2

解题思路

分析

首先肯定不能用暴力来做,不超时才有鬼。
对于处理一个数组,将其区间+或-某个数的时候,有两种方法:线段树或者差分。

差分更新的时间复杂度是 O ( 1 ) O(1) O(1),查询的时间复杂度是 O ( n ) O(n) O(n)
线段树更新的时间复杂度是 O ( l o g n ) O(logn) O(logn),查询的时间复杂度也是 O ( l o g n ) O(logn) O(logn)

因此这道多次更新,最后一次查询的题,应该是使用差分来做。

差分介绍

差分数组就是存储相邻两个数的差,对于数组 a [ 1 ⋯ n ] a[1\cdots n] a[1n],其差分数组b的计算公式是:

b [ 1 ] = a [ 1 ] , b [ i ] = a [ i ] − a [ i − 1 ] ( i > 1 ) b[1]=a[1],b[i]=a[i]-a[i-1](i>1) b[1]=a[1],b[i]=a[i]a[i1](i>1)

将其转化为原数组的方法就是求前缀和,即 ∑ i = 1 j b [ j ] = a [ j ] \sum_{i=1}^{j}{b[j]}=a[j] i=1jb[j]=a[j]

可见,差分数组存储的是原数组的差。那这东西有什么用呢?

我们先假设存在a数组与差分数组b如下:

数组12345
a52314
b5-31-23

我们对于a数组区间 [ l , r ] [l,r] [l,r]上的操作,可以转化为对其差分数组b中 b [ l ] b[l] b[l] b [ r + 1 ] b[r+1] b[r+1]的操作。如,对区间 [ 2 , 4 ] [2,4] [2,4]上的所有元素+3,可以转化为 b [ 2 ] + 3 , b [ 5 ] − 3 b[2]+3,b[5]-3 b[2]+3,b[5]3,a数组和b数组更改后如下所示:

数组12345
a55644
b501-26

此时的b数组仍然是更改后a数组的差分,所以, a [ l ⋯ r ] + c a[l\cdots r]+c a[lr]+c 等价于 b [ l ] + c , b [ r + 1 ] − c b[l]+c,b[r+1]-c b[l]+c,b[r+1]c

这样,每次对区间的操作,都可以转化为对b数组两个点的操作,大大提升了更新效率。最后将b求前缀和就可以得到操作完成的a数组。

差分的原理

那为什么可以样操作呢?关键在于最后b数组转化为a数组的过程是一个求前缀和的过程。

如果我们将b数组中 b [ l ] + c b[l]+c b[l]+c,那么从 l l l开始向后的所有元素,在求前缀和的时候,都将这个c加进去了,所以结果就是区间 [ l , n ] [l,n] [l,n]都加上了c。

我们将 b [ r + 1 ] − c b[r+1]-c b[r+1]c,那么从 r + 1 r+1 r+1开始,向后的所有元素在求前缀和的时候,都经历了一个 + c +c +c后又 − c -c c的过程,也就是没有变化。

经过上面两个操作,我们相当于将 a [ l ] a[l] a[l] a [ r ] a[r] a[r]之间到所有元素都加上了c。

完整代码

//#pragma GCC optimize(2)//比赛禁止使用!
//#pragma G++ optimize(2)
//#include <bits/stdc++.h>
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <string>
#include <climits>
#include <algorithm>
#include <queue>
#include <vector>
using namespace std;

const int maxn=200000+10;
int n,q,a[maxn],b[maxn];
//a是原始数据,b是差分数组
int getint()
{
    int x=0,s=1;
    char ch=' ';
    while(ch<'0' || ch>'9')
    {
        ch=getchar();
        if(ch=='-') s=-1;
    }
    while(ch>='0' && ch<='9')
    {
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x*s;
}
int main()
{
    n=getint(); q=getint();
    for (int i=1; i<=n; i++)
        a[i]=getint();
    for (int i=1; i<=n; i++)//求差分
        b[i]=a[i]-a[i-1];
    for (int i=1; i<=q; i++)
    {
        int l=getint(),r=getint(),c=getint();
        b[l]+=c; b[r+1]-=c;
    }
    long long ans=0;
    for (int i=1; i<=n; i++)//前缀和将差分转换回去
    {
        ans+=b[i];
        printf("%lld ",ans);
    }
    putchar('\n');

    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值