前缀和、差分

0x03前缀和、差分

一维前缀和

数组前n项和

s [ k ] = ∑ i = 1 k a [ i ] s[k]=\sum_{i=1}^ka[i] s[k]=i=1ka[i]

s[i]=s[i-1]+a[i];

二维前缀和

s[i][j]表示以(1,1)为顶点,以(i,j)为右下角的矩形内部权值之和。

即: s [ n ] [ m ] = ∑ i = 1 n ∑ j = 1 m a [ i ] [ j ] s[n][m]=\sum_{i=1}^{n}\sum_{j=1}^ma[i][j] s[n][m]=i=1nj=1ma[i][j]

a[i][j](i,j)点的权值。

那么s[i][j]可以表示为:

s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+a[i][j];

假如我们要求矩阵中 R x R 的矩形的权值和:

ans = s[i+R][j+R]-s[i-1][j+R]-s[i+R][j-1]+s[i-1][j-1];

差分

差分<-------->原数组<------->前缀和

原数组是差分和前缀和的中转站。

一维差分

b[i]=a[i]-a[i-1]

显然,将b[i]累加起来就是a[i]

故有:

∑ i = 1 j b [ i ] = a [ j ] \sum_{i=1}^jb[i]=a[j] i=1jb[i]=a[j]

所以:

for(int i=1;i<=n;i++){
   b[i]+=b[i-1];
}

累加之后,b[i]就等于a[i].

故,如果我们需要对一个区间进行增减同一个数x的操作

只需要对这个区间的差分数组进行单点修改。

例如,我们需要将[2,5]内的元素全部都增加1

只需要让b[2]+1, b[6]-1 即可。

二维差分

∑ i = 1 n ∑ j = 1 m b [ i ] [ j ] = a [ i ] [ j ] \sum_{i=1}^n\sum_{j=1}^mb[i][j]=a[i][j] i=1nj=1mb[i][j]=a[i][j]

当我们要对一个矩阵中的某一块区域进行加减同一个数x的操作的时候:

例如,将如下这个区间所有元素加上1:

在这里插入图片描述
差分数组:
在这里插入图片描述

其实和一维差分差不多。


习题

T1

99. 激光炸弹 - AcWing题库

普通的二维前缀和,如果为了节省空间的话,可以用前缀和数组和原数组公用一个数组。

#include<bits/stdc++.h>
using namespace std;
const int N = 5000+10;
typedef long long ll;
int sum[N][N] = { 0 };
int main() {
   ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
    int n;
    int R;
    cin >> n >> R;
    R=min(5001,R);
    R--;
    memset(sum, 0, sizeof sum);
    for (int i = 1; i <= n; i++) {
        int x, y, v;
        cin >> x >> y >> v;
        x++;
        y++;
        sum[x][y] += v;
    }
   
    for (int i = 1; i <= 5000+1; i++) {
        for (int j = 1; j <= 5000+1; j++) {
            sum[i][j] += sum[i - 1][j] + sum[i][j - 1] - sum[i-1][j-1];
        }
    }

    int ans = 0;
    for (int i = 1; i <= 5000+1; i++) {
        for (int j = 1; j <= 5000+1; j++) {
            if(i+R>5001 or j+R>5001)continue;
             ans = max(ans, sum[i+R][j+R] - sum[i-1][j+R] - sum[i+R][j-1] + sum[i-1][j-1]); 
      }
    }

    cout << ans;
}

T2

100. 增减序列 - AcWing题库

差分的经典用途:将某一个区间[l,r]内的数增减同一个数x。

只需要在其差分数组上b[l] 减去1, b[r+1]加上1

所以只对其差分数组中的两个点进行修改。

理论上,如果一个序列中的所有的数是一样的话,那么其差分数组

除第一项以外,其余的都是0.

所以我们只需要修改 b[2]~b[n]中的任意两项,使b[2]~b[n]都为0

计算, b[2]~b[n]中,正数和p、负数和的绝对值q。然后取最小的抵消。此时剩下p-q

进行的操作是min(p,q)

然后剩下的数,与b[1]b[n+1]进行操作。

1、如果b[1]有操作,那么b[1]只可能会被操作 1~p-q次

所以有 p-q种可能

2、如果b[1]无操作,说明p-q次操作都在b[n+1]上,那么有1种情况

总共就是p-q+1 种情况

总操作数:max(p,q)

#include<bits/stdc++.h>
using namespace std;
#define  ios ios::sync_with_stdio(false),cin.tie(nullptr),cout.tie(nullptr) 
#define lson pos<<1
#define rson (pos<<1)|1
const int N = 1e5+7;
typedef long long ll;
ll a[N];
ll b[N];
int main() {
    ios;
   int n;
   cin>>n;
   for(int i=1;i<=n;i++)cin>>a[i];
   for(int i=1;i<=n;i++)b[i]=a[i]-a[i-1];
   ll zsum=0;
   ll fsum=0;
   for(int i=2;i<=n;i++){
       if(b[i]>0)zsum+=b[i];
       if(b[i]<0)fsum+=b[i];
   }
   fsum=-fsum;
   int ans=max(zsum,fsum);
   int bns=abs(zsum-fsum);
   cout<<ans<<endl<<bns+1;
  
}


T3

101. 最高的牛 - AcWing题库

差分的另一种运用,我们一开始假设这些牛一样高,

若 l、r 能互相看见,说明 l、r 之间的数都需要-1

然后最终的答案就是最大身高。

为了减少时间复杂度,我们用差分数组进行单点修改。

将a[l+1]-1, a[r]+1

然后求和,然后每一项+h即可

#include<bits/stdc++.h>
using namespace std;
#define  ios ios::sync_with_stdio(false),cin.tie(nullptr),cout.tie(nullptr) 
const int N=5e3+7;
typedef long long ll;
map<pair<int,int>,int > mmp;

int a[N];
int main() {
    ios;
   int n,p,h,m;
   cin>>n>>p>>h>>m;
  // for(int i=1;i<=n;i++)a[i]=h;
   while(m--){
       int l,r;
       cin>>l>>r;
       if(l>r)swap(l,r);
       if(mmp[pair(l,r)]==0){
       mmp[pair(l,r)]++;
       a[l+1]--;
       a[r]++;
     }
   }
   for(int i=1;i<=n;i++){
       a[i]+=a[i-1];
   }
   for(int i=1;i<=n;i++){
       a[i]+=h;
   }
   for(int i=1;i<=n;i++)cout<<a[i]<<endl;
}


  • 18
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

louisdlee.

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

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

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

打赏作者

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

抵扣说明:

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

余额充值