• 【蓝桥每日一题]-前缀和与差分(保姆级教程 篇1)


    本篇是模板篇

    目录

    简介:

    前缀和:

    差分:

          


          

          

    简介:

    前缀和:

    前缀和指一个数组的某下标之前的所有数组元素的和(即数列的前n项求和),前缀和是一种重要的预处理,能够降低算法的时间复杂度,可以快速地求出某一段的和,对于处理区间之间的问题是往往十分高效

    差分:

    差分实际上就是构建一个数组,让原数组是差分数组的前缀和数组

         

    额,暂且就讲这么直白点吧,讲的太深了你又听不懂了。只要知道什么时候去用它们就行了。最重要的还是在应用中去体会!

         


             

           

    上模板:

    前缀和:

    前缀和用于快速求区域的元素和

    (注意:前缀和数组,我建议你一定要开long long,不然容易越界) 

           

        

    一维前缀和:

    1. for(int i=1;i<=n;i++)
    2. suf[i]=suf[i-1]+a[i];

    我建议你a[0]不要放元素,这样子初始化更方便,哈哈哈

            

    二维前缀和:

    这个是创建前缀和的一个从(1, 1)到(i,j)的 “和矩阵”。

    比如suf[1][1]就直接等于a[1][1],suf[2][2]就等于a[1][1],a[1][2],a[2][1],a[2][2],类似这种的矩阵 

    1. void create_suf(){
    2. for(int i=1;i<=3;i++)
    3. for(int j=1;j<=3;j++){
    4. suf[i][j]=a[i][j]+suf[i-1][j]+suf[i][j-1]-suf[i-1][j-1];
    5. }
    6. }

    我也建议你a[ ][ ]也是从(1,1)开始放元素,这样初始化不需要考虑越界问题,很方便。

          

    这个是获取从(x1,y1)到(x2,y2)对角线内元素的和,注意是闭区间的(我已经打注释了)

    1. int get_suf(int x1,int y1,int x2,int y2){//左右都是闭区间
    2. return suf[x2][y2]-suf[x2][y1-1]-suf[x1-1][y2]+suf[x1-1][y1-1];
    3. }

            

    上面两部分就是核心代码(俗称板子,这是我最常用的板子,经过优化后,还算简短美观的!)!好了,下面是完整的代码

    1. #include
    2. using namespace std;
    3. typedef long long ll;
    4. int a[4][4]; //二维前缀和(起点必须从1,1开始,这样简单)
    5. ll suf[4][4];
    6. void create_suf(){
    7. for(int i=1;i<=3;i++)
    8. for(int j=1;j<=3;j++){
    9. suf[i][j]=a[i][j]+suf[i-1][j]+suf[i][j-1]-suf[i-1][j-1];
    10. }
    11. }
    12. int get_suf(int x1,int y1,int x2,int y2){//左右都是闭区间
    13. return suf[x2][y2]-suf[x2][y1-1]-suf[x1-1][y2]+suf[x1-1][y1-1];
    14. }
    15. int main(){
    16. int cnt=1;
    17. for(int i=1;i<=3;i++){
    18. for(int j=1;j<=3;j++){
    19. a[i][j]=cnt++;//给a数组随机赋值
    20. }
    21. }
    22. create_suf();
    23. cout<<get_suf(2,2,3,3)<<'\n'<<get_suf(1,2,2,3);
    24. }

         

          

          

    差分:

    差分用于求数据多次变动后的结果,差分是前缀和的进阶用法,但公式差不多,你可以仔细体会一下

      (注意: suf数组必须开ll       dif数组必须再多开一层        二维前缀和或差分必须从1,1开始

          

          

    一维差分:

    核心模板:数据变动部分(从x下标到y下标都加z)

    1. void add(int x,int y,int z){
    2. suf[x]+=z;suf[y+1]-=z;//因为x到y都要加z
    3. }

    suf要多开一个

            

    完整代码 :(相当于一口气把3个数据变化操作全做了,这就是为什么差分快速的原因)

    1. #include
    2. using namespace std;
    3. typedef long long ll;
    4. ll suf[6]; //这个数组既是dif也是suf,必须多开一层,且用ll
    5. void add(int x,int y,int z){
    6. suf[x]+=z;suf[y+1]-=z;//因为x到y都要加z
    7. }
    8. int main(){
    9. int a[5]={1,2,3,4,5}; //一维差分
    10. add(2,4,5);//从2到5的元素都加5
    11. add(1,3,2);//从1到3元素都加2
    12. add(0,2,-3);//都-3
    13. for(int i=1;i<5;i++){
    14. suf[i]+=suf[i-1];
    15. }
    16. for(int i=0;i<5;i++){
    17. a[i]+=suf[i];
    18. }
    19. }

          

          

    二维差分:

    模板:这里是数据变动部分(从(x1, y1)到(x2, y2)都变动 z )

    1. void add(int x1,int y1,int x2,int y2,int z){ //dif的作用在后面元素(可能用不到),所以才让你多开一层
    2. dif[x1][y1]+=z;dif[x2][y2+1]-=z;dif[x2+1][y2]-=z;dif[x2+1][y2+1]+=z;
    3. }

     注意:我也注释了,就是dif总要在x2和y2后面加上对前面加z的抵消,故才让你多开一层dif数组

             

     这里是由dif数组转为suf数组,相当于一口气处理了所有的数据变动,然后再加给原数组即可

    1. void create_suf(){
    2. for(int i=1;i<=3;i++)
    3. for(int j=1;j<=3;j++){
    4. suf[i][j]=dif[i][j]+suf[i-1][j]+suf[i][j-1]-suf[i-1][j-1];
    5. }
    6. for(int i=1;i<=3;i++)
    7. for(int j=1;j<=3;j++){
    8. a[i][j]+=suf[i][j];
    9. }
    10. }

    都是前缀和那里的模板,不说了 

             

     上面就是模板,下面是完整代码:

    1. #include
    2. using namespace std;
    3. typedef long long ll;
    4. int a[4][4],dif[5][5]; //dif要多开一层
    5. ll suf[4][4];
    6. void add(int x1,int y1,int x2,int y2,int z){ //dif的作用在后面元素(可能用不到),所以才让你多开一层
    7. dif[x1][y1]+=z;dif[x2][y2+1]-=z;dif[x2+1][y2]-=z;dif[x2+1][y2+1]+=z;
    8. }
    9. void create_suf(){
    10. for(int i=1;i<=3;i++)
    11. for(int j=1;j<=3;j++){
    12. suf[i][j]=dif[i][j]+suf[i-1][j]+suf[i][j-1]-suf[i-1][j-1];
    13. }
    14. for(int i=1;i<=3;i++)
    15. for(int j=1;j<=3;j++){
    16. a[i][j]+=suf[i][j];
    17. }
    18. }
    19. int main(){
    20. int cnt=1;
    21. for(int i=1;i<=3;i++){ //从(1,1)开始存
    22. for(int j=1;j<=3;j++){
    23. a[i][j]=cnt++;
    24. }
    25. }
    26. add(1,1,3,2,3);
    27. add(2,2,3,3,-1);
    28. create_suf();
    29. for(int i=1;i<=3;i++){
    30. for(int j=1;j<=3;j++) cout<' ';
    31. cout<<'\n';
    32. }
    33. }

    以后就开始练题了,不管是什么事情,实操才是最重要的

  • 相关阅读:
    Doris学习笔记之备份与恢复
    Vue-01-前端背景介绍
    大文件上传demo,前端基于Uppy,后端基于koa
    外键必须是另一个表的主键吗 ?
    商业智能BI业务分析思维:现金流量风控分析(一)营运资金风险
    【ContextCapture建模精品教程】三维实景模型生成集群设置(2)——工程文件网络路径设置
    大语言模型(一)OLMo
    进程程序替换
    ELK部署Redis+Keepalived高可用环境
    基于php js+mysql+laravel技术架构的手术麻醉管理系统源码 手麻系统源码
  • 原文地址:https://blog.csdn.net/m0_69478376/article/details/134083350