当前位置:首页 > 记忆回溯 > 正文内容

前缀和 & 差分

MuWinds1个月前 (02-05)记忆回溯24

C++ 标准库中实现了前缀和函数 std::partial_sum,定义于头文件 <numeric> 中。

一个经典的例题:

image.png

暴力当然直接TLE

//这段代码是能拿50分的,大规模会有超时问题
//没有很好的利用前缀和
#include<bits/stdc++.h>
using namespace std;
int main(){
    int n;
    cin>>n;
    vector<int>a;
    for(int i=0;i<n;i++){
        int t;cin>>t;
        a.push_back(t);
    }
    int m;
    cin>>m;
    for(int i=0;i<m;i++){
        int l,r,t=0;
        cin>>l>>r;
        for(int j=l-1;j<=r-1;j++)t+=a[j];
        cout<<t<<endl;
    }
    return 0;
}

将上面代码的加起来的过程给优化一下,在输入阶段就将数组改写成前缀和的形式,输出的时候只要s[r]-s[l-1]就可以了

#include<bits/stdc++.h>
using namespace std;
int main(){
    int n;
    cin>>n;
    vector<int>a;a.push_back(0);
    for(int i=1;i<=n;i++){
        int t;cin>>t;
        a.push_back(t+a[i-1]);
    }
    int m;cin>>m;
    for(int i=0;i<m;i++){
        int l,r;
        cin>>l>>r;
        cout<<a[r]-a[l-1]<<endl;
    }
    return 0;
}

从一维扩展到二维,思考方式就多了

一个是基于容斥原理,什么是容斥原理?这涉及到数学上集合的概念了。先看下面的图:

undefined

现在给了A,B,C三个集合的元素数量,要求你求出至少在一个元素的数量,他不能是直接把三个集合的元素加起来,必须得把重复的元素给扣掉,计算这个数量的过程就是容斥原理。

现在回到二维前缀和的部分,给定大小为 m *n 的二维数组 A,要求出其前缀和 S。那么,S 同样是大小为 m* n 的二维数组,且

image.png

类比一维的情形,S{i,j} 应该可以基于 S{i-1,j} 或 S{i,j-1} 计算,从而避免重复计算前面若干项的和。但是,如果直接将 S{i-1,j} 和 S{i,j-1} 相加,再加上 A{i,j},会导致重复计算 S{i-1,j-1} 这一重叠部分的前缀和,所以还需要再将这部分减掉。这就符合了容斥原理。由此得到如下递推关系:

image.png

实现时,直接遍历 (i,j) 求和即可。

image.png

同样的道理,在已经预处理出二位前缀和后,要查询左上角为 (i[1],j[1])、右下角为 (i[2],j[2]) 的子矩阵的和,可以计算

image.png

这可以在 O(1) 时间内完成。

在二维的情形,以上算法的时间复杂度可以简单认为是 O(mn),即与给定数组的大小成线性关系。但是,当维度 k 增大时,由于容斥原理涉及的项数以指数级的速度增长,时间复杂度会成为 O(2^kN),这里 k 是数组维度,而 N 是给定数组大小。因此,该算法不再适用。

接下来是差分:
差分的定义:在多张图画中,主体结构不变,展现其他较小变化的多张图片,可称其互为差分。

在算法中,他是一种和前缀和相对的策略

image.png

这里的a[i]即数组中每个元素

image.png

它可以维护多次对序列的一个区间加上一个数,并在最后询问某一位的数或是多次询问某一位的数。注意修改操作一定要在查询操作之前。

image.png

例题:

image.png思路

这题明显就是差分

根据差分数组的定义: d[i]=a[i]-a[i-1],可以发现,在d[x]上加上z,会让后面的学生全部加上z。↓

但这是把后面全部都加了,还要减掉一节,所以↓

把两个综合起来就是↓

所以每一次变化只要把d[x]+z,d[y+1]-z就好了。

因为 d[i]=a[i]-a[i-1]

所以 a[i-1]+d[i]=a[i]

最后再根据 a[i-1]+d[i]=a[i]输出每一个同学的分数

题解:

#include<bits/stdc++.h>
using namespace std;
int main(){
    int n,p;
    cin>>n>>p;
    vector<int>a(n+1,0),d(n+1,0);//动态分配内存的数组
    for(int i=1;i<=n;i++){
        cin>>a[i];
        d[i]=a[i]-a[i-1];//差分
    }
    int x,y,z;
    for(int i=0;i<p;i++){
        cin>>x>>y>>z;
        d[x]+=z,d[y+1]-=z;
    }
    int min=300;
    //找出分数最少的学生
    for(int i=1;i<=n;i++){
        a[i]=a[i-1]+d[i];
        if(min>a[i])min=a[i];
    }
    //经过上面的操作,将时间复杂度给干到O(n)了
    cout<<min<<endl;
    return 0;
}





“前缀和 & 差分” 的相关文章

PEP8 PYTHON 編碼規範手冊

PEP 8 介紹PEP8 是 Python 社群共通的風格指南,一開始是 Python 之父 Guido van Rossum 自己的撰碼風格,慢慢後來演變至今,目的在於幫助開發者寫出可讀性高且風格一致的程式。許多開源計畫,例如 Django 、 OpenSt...

PHP 程式碼風格PSR

PSR-1:基本程式寫作標準1. 總覽檔案只能使用 <?php 和 <?= 標籤檔案字元編碼只能用 UTF-8 檔首無 BOM檔案應該只宣告符號 (class、function、constant)或是造成副作用(例如產生輸出、修改 .ini 檔之類)兩者擇一不應該兩個都做Namespac...

go gin框架实现限流

接手一个项目,甲方希望在项目中实现指定用户的限流,这需要我们在go项目中实现一个限流器。限流器的实现有很多经典的思想,不过令牌桶的思路简单,运行效率高,它是以恒定的速度往木桶里加入令牌,木桶满了则不再加入令牌。服务收到请求时尝试从木桶中取出一个令牌,如果能够得到令牌则继续执行后续的业务逻辑。如果没有...

AMD Ryzen Windows睡死问题解决

更新AMD芯片组驱动(不分任何笔记本厂家): https://seaou.lanzouw.com/iQL0V2m7wnuh...

数据结构——堆(Heap)大根堆、小根堆

转载:https://www.cnblogs.com/wangchaowei/p/8288216.htmlHeap是一种数据结构具有以下的特点:1)完全二叉树;2)heap中存储的值是偏序;Min-heap: 父节点的值小于或等于子节点的值;Max-heap: 父节点的值大于或等于子节点的值;堆的存...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。