基础算法-前缀和&差分

前缀和

顾名思义,前缀和表示求当前前缀之和,一般应用于求一段区间的数之和。
假设我们存数的数组为a[],前缀和数组为sum[],那么sum[i]=sum[i-1]+a[i],表示本位的前缀和为前一位的前缀和加上本位的值。
那么求一段区间[l,r]之和便可以表示为sum[r]-sum[l-1],是一个非常好的预处理方法。

例题一:前缀和

输入一个长度为 n 的整数序列。
接下来再输入 m 个询问,每个询问输入一对 l,r。
对于每个询问,输出原序列中从第 l 个数到第 r 个数的和。

输入格式

第一行包含两个整数 n 和 m。
第二行包含 n 个整数,表示整数数列。
接下来 m 行,每行包含两个整数 l 和 r,表示一个询问的区间范围。

输出格式

共 m 行,每行输出一个询问的结果。

前缀和模板:

#include "bits/stdc++.h"
using namespace std;
const int N = 100010;
int a[N],sum[N],n,m;
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++) cin>>a[i];
    for(int i=1;i<=n;i++) sum[i]=sum[i-1]+a[i];
    while (m -- ){
        int l,r;
        cin>>l>>r;
        cout<<sum[r]-sum[l-1]<<endl;
    }
}

二维前缀和

前缀和是简单的在一个一维数组上进行前缀求和,那二维前缀和顾名思义便是在二维数组上进行前缀求和。
那么对于二维数组中的点(i,j)来说,它的前缀便是行(1~i) 列(1~j)所组成的方形。
那二维数组前缀和可表示为sum[i][j]=a[i][j]+sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]
当前这个点再加上前一行的前缀和,再加上前一列的前缀和,再减去重复加的部分。
二维前缀和
如图,我们在求解二维数组从点l1,r1到点l2,r2之间数之和时,便可以表示为
res=sum[l2][r2]-sum[l1-1][r2]-sum[l2][r1-1]+sum[l1-1][r1-1]

例题二:Counting Rectangles(codeforces 1722E)

You have n rectangles, the i-th rectangle has height hi and width wi.

You are asked q queries of the form hs ws hb wb.

For each query output, the total area of rectangles you own that can fit a rectangle of height hs and width ws while also fitting in a rectangle of height hb and width wb. In other words, print ∑hi⋅wi for i such that hs<hi<hb and ws<wi<wb.

Please note, that if two rectangles have the same height or the same width, then they cannot fit inside each other. Also note that you cannot rotate rectangles.

题目大意:

第一行给我们n,q。
给我们n个点(h,w),每个点的权值为h*w。
再给出q个询问,每个询问给出hs,ws,hb,wb.
问在(hs,hb),(ws,wb)间的点的权值之和是多少。

输入格式

第一行包含一个整数 t,表示样例个数。
第二行包含 2个整数,n,q。
接下来 n 行,每行包含两个整数 h 和 w,表示一个点的位置。
接下来 q 行,每行包含四个整数 hs,ws,hb,wb。

输出格式

共 m 行,每行输出一个询问的结果。

二维前缀和模板:

#include "bits/stdc++.h"
#define int long long
#define mod 1000000007
using namespace std;
const int N=100010;
int t,n,res,q,m[1010][1010],ans[1010][1010];
inline int read()//快读
{
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9')f=(ch=='-'?-1:1),ch=getchar();
	while(ch>='0'&& ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
	return x*f;
}
inline void write(int x) //快写
{
     if(x<0) putchar('-'),x=-x;
     if(x>9) write(x/10);
     putchar(x%10+'0');
}
signed main()
{
    //ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
    t=read();
    while(t--)
    {
        memset(m,0,sizeof m);
        memset(ans,0,sizeof ans);
        n=read();q=read();
        while(n--)
        {
            int j,k;
            cin>>j>>k;
            m[j][k]+=j*k;
        }
        for(int i=1;i<=1000;i++)
            for(int j=1;j<=1000;j++)
            {
                ans[i][j]=m[i][j]+ans[i-1][j]+ans[i][j-1]-ans[i-1][j-1];
            }
        while(q--)
        {
            int l,r,ll,rr;
            l=read();r=read();ll=read();rr=read();
            cout<<ans[ll-1][rr-1]-ans[ll-1][r]-ans[l][rr-1]+ans[l][r]<<endl;
        }
    }
}

前缀和可以理解成对前缀有影响
差分可以理解成对后缀有影响

差分

差分表示的是当前数与前一个数之差,一般用于求解一段区间加/减上一个数。
差分可以看作是前缀和的逆向,如果做一遍差分再做一遍前缀和的话便会回到原来的样子。
假设我们存数的数组为a[],差分数组为sub[],那么sub[i]=a[i]-a[i-1]。表示本位与前一位的差分值。
那么对一段区间l,r加上一个数x,便可以表示为sub[l]+=x;sub[r+1]-=x;
那么怎么理解呢,由于我们是先差分后做前缀和来完成一段区间的加减,那么sub[l]+=x在后面做前缀和的时候便是从l到末尾都加上了x,由于我们只需要[l,r]加x,故sub[r+1]-=x;

例题三: 差分

输入一个长度为 n 的整数序列。
接下来输入 m 个操作,每个操作包含三个整数 l,r,c,表示将序列中 [l,r] 之间的每个数加上 c。
请你输出进行完所有操作后的序列。

输入格式

第一行包含两个整数 n 和 m。
第二行包含 n 个整数,表示整数序列。
接下来 m 行,每行包含三个整数 l,r,c,表示一个操作。

输出格式

共一行,包含 n 个整数,表示最终序列。

差分模板:

#include "bits/stdc++.h"
using namespace std;
const int N = 100010;
int n,m,a[N],sub[N];
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++) cin>>a[i];
    for(int i=1;i<=n;i++) sub[i]=a[i]-a[i-1];
    while (m -- ){
        int l,r,x;
        cin>>l>>r>>x;
        sub[l]+=x;
        sub[r+1]-=x;
    }
    for(int i=1;i<=n;i++) 
    {
        sub[i]+=sub[i-1];
        cout<<sub[i]<<' ';
    }
}

二维差分

二维差分顾名思义便是差分在二维数组上的实现,差分我们可以理解成对后缀有影响,那么二维矩阵的后缀是什么呢。
对于二维数组上的点(i,j)来说,它的后缀便是行[i,n]列[j,m]组成的方形。
二维差分数组的构造表示为:sub[i][j]=a[i][j]-a[i-1][j]-a[i][j-1]+a[i-1][j-1]
那如何表示在一个子矩阵上的加减呢,如下图:
二维差分矩阵
如果我们要在橙色子矩阵上进行加减,那么我们表示为:
sub[l1][r1]+=x;sub[l1][r2+1]-=x;sub[l2+1][r1]-=x;sub[l2+1][r2+1]+=x;
左上角加上x,再减去多的部分,再加回重复减的部分。

例题四: 差分矩阵

输入一个 n 行 m 列的整数矩阵,再输入 q 个操作,每个操作包含五个整数 x1,y1,x2,y2,c,其中 (x1,y1) 和 (x2,y2) 表示一个子矩阵的左上角坐标和右下角坐标。
每个操作都要将选中的子矩阵中的每个元素的值加上 c。
请你将进行完所有操作后的矩阵输出。

输入格式

第一行包含整数 n,m,q。
接下来 n 行,每行包含 m 个整数,表示整数矩阵。
接下来 q 行,每行包含 5 个整数 x1,y1,x2,y2,c,表示一个操作。

输出格式

共 n 行,每行 m 个整数,表示所有操作进行完毕后的最终矩阵。

二维差分模板:

#include "bits/stdc++.h"
using namespace std;
const int N = 1010;
int a[N][N],sub[N][N],n,m,q;
int main()
{
    cin>>n>>m>>q;
    for(int i=1;i<=n;i++) 
        for(int j=1;j<=m;j++) 
        {
            cin>>a[i][j];
            sub[i][j]=a[i][j]-a[i-1][j]-a[i][j-1]+a[i-1][j-1];
        }
    while(q--){
        int l1,r1,l2,r2,x;
        cin>>l1>>r1>>l2>>r2>>x;
        sub[l1][r1]+=x;
        sub[l1][r2+1]-=x;
        sub[l2+1][r1]-=x;
        sub[l2+1][r2+1]+=x;
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            sub[i][j]=sub[i][j]+sub[i-1][j]+sub[i][j-1]-sub[i-1][j-1];
            cout<<sub[i][j]<<' ';
        }
        cout<<endl;
    }
}
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

indolence-阡陌

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值