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

问题描述

给定数组a,给定操作q个操作: l , r , c l,r,c ,代表将a数组从 a [ l ] a[l] 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) ,代表数组有n个元素,q个操作
第二行输入数组a,其中 1 0 6 a i 1 0 6 -10^6\le a_i\le 10^6
后面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)

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 ( n ) O(n)
线段树更新的时间复杂度是 O ( l o g n ) O(logn) ,查询的时间复杂度也是 O ( l o g n ) O(logn)

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

差分介绍

差分数组就是存储相邻两个数的差,对于数组 a [ 1 n ] a[1\cdots n] ,其差分数组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)

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

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

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

数组 1 2 3 4 5
a 5 2 3 1 4
b 5 -3 1 -2 3

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

数组 1 2 3 4 5
a 5 5 6 4 4
b 5 0 1 -2 6

此时的b数组仍然是更改后a数组的差分,所以, a [ l r ] + c a[l\cdots r]+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 ,那么从 l l 开始向后的所有元素,在求前缀和的时候,都将这个c加进去了,所以结果就是区间 [ l , n ] [l,n] 都加上了c。

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

经过上面两个操作,我们相当于将 a [ l ] a[l] 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;
}
发布了32 篇原创文章 · 获赞 24 · 访问量 2229

猜你喜欢

转载自blog.csdn.net/weixin_43347376/article/details/105028618
今日推荐