差分、前缀和

差分

区间加:把数组a[l]到a[r]都加上k,这种操作称为区间加。

如果一般朴素的想法应该是下面这样的:

    int a[10005],l,r,k;
    for(int i=l;i<=r;i++){
        a[i]+=k;
    } 

但可以发现如果是l-r非常大的话,这个操作执行的次数又很多,那时间复杂度会很高(其实是我不会算)

所以我们通过引入差分的概念,简化这个问题。

差分:差分即相邻两个数的差。我们用一个数组p存储a的差分,那么p是a的差分数组,即pi=ai-ai-1  。

这张图可以帮助更好地理解差分的概念

那么通过差分,如果继续做区间加操作的话,只需要像下面这样。

首先是查分数组的初始化:

    int p[10],a[10]={0,1,2,3,90,5,6,7,8,9};
        //p是a的差分数组 
    for(int i=1;i<=9;i++){
        p[i]=a[i]-a[i-1];
        cout<<p[i]<<" ";
    }

这段代码会输出

输出: 1 1 1 87 -85 1 1 1 1 

那么我们现在就可以进行区间加操作了,代码如下:

int p[10],a[10]={0,1,2,3,90,5,6,7,8,9};
void pl(int l,int r,int k){
    //操作:在[l,r]上加 k
    p[l]+=k,p[r+1]-=k;
        //注意是 r+1 !!! 
    return; 
}

那么当我们要输出数组a的某一个元素要怎么办呢

很简单,通过差分数组p和原来的数组a“倒推”出a[i],代码实现如下

void get_a(){
    for(int i=1;i<=9;i++){
        a[i]=p[i]+a[i-1];
    }
}

前缀和

前缀和其实就是用一个数组s来记录数组a前i项的和,有点类似数列的求和。也可用来求区间和,如求数组a在区间[l,r]的和,就可以s[r]-s[l-1](注意要减1哦!!!!!)

我们在输入a数组时可以预处理一下,代码如下。

    int a[11],s[11];
    a[0]=0,s[0]=a[0];
    
    for(int i=1;i<=10;i++){
        a[i]=i*i;
        s[i]=s[i-1]+a[i];
        cout<<a[i]<<" ";
    }
    cout<<endl;
    for(int i=1;i<=10;i++){
        cout<<s[i]<<" ";
    }
    cout<<endl;

输出结果如下:

1 4 9  16 25 36 49  64  81  100 
1 5 14 30 55 91 140 204 285 385 

那么区间[l,r]的和就是下面这样:

int qjh(int l,int r){
    return s[r]-s[l-1];
}

cout<<qjh(3,6);

这里会输出

86

 

posted @ 2019-07-25 16:14  jyhzf  阅读(4443)  评论(3编辑  收藏  举报