前缀和(一维、二维)

前缀和算法可以在 O(1) 的时间复杂度下查询一个集合中的一个连续子集中的所有元素之和。

一维前缀和:

一维前缀和可以在 O(1) 的时间复杂度下求序列的任意连续范围内的所有元素之和。

假设数组 a ,数组 s 为其前缀和数组 ,则 s[i] 表示 a[1]~a[i] 之和;

假设左端点 l ,右端点 r ,求 a[l]~a[r] 的和,只需求 s[r] - s[l-1] 。

 代码模板如下:

for (int i = 1; i <= n; i++) {//预处理出前缀和数组
    s[i] += s[i - 1];
 
while (m--) {//求m次任意区间的和
    cin >> l >> r;
    cout << s[r] - s[l - 1] << endl;
}

二维前缀和:

二维前缀和可以在 O(1) 的时间复杂度下求矩阵的任意子矩阵中的所有元素之和。

设数组 s ,s[i][j] 表示以 (1,1) 为左上角,(i , j) 为右下角的矩阵中的所有元素之和;

求以 (m , n) 为左上角,(p , q) 为右下角的矩阵中所有元素之和,只需求 s[p][q] - s[p][m-1] - s[n-1][q] + s[m-1][n-1]。

图示:

代码模板如下:

for (int i = 1; i <= n; i++)//构造前缀和
    for (int j = 1; j <= m; j++) {
        cin>>a[i][j];
        s[i][j] = a[i][j] + s[i][j - 1] + s[i - 1][j] - s[i - 1][j - 1];
    }

    while (k--){//求k次求矩阵元素之和( (a,b)~(c,d) )
        cin>>a>>b>>c>>d;
        cout << s[c][d] - s[a - 1][d] - s[c][b - 1] + s[a - 1][b - 1] << endl;
    }

例题1. 截断数组:

3956. 截断数组 - AcWing题库

思路:

首先,求数组中一段元素之和,用前缀和算法时间复杂度为O(1);

将数组分为三段需要截断两处,如果我们枚举两处的位置,时间复杂度为O(n^2),总体时间复杂度也为O(n^2);

尝试只枚举一处;

假设数组内所有元素之和为sum,那么分为的三段每段中的元素之和为sum/3;

一重循环遍历所有数,假设指针指向i;

如果s[i-2](区间1取[1,i-2],区间3取[i,n])==sum/3,这时就找到了一段第一段所有元素之和等于sum/3的区间(记录下找到的满足所需的第一段的个数,cnt++);

此时再找出第三段所有元素之和也为sum/3的区间即满足了三段均为sum/3,即s[n]-s[i-1] == sum/3;

图示:

代码如下:

if(s[n]%3){cout<<"0";return 0;}    

long long res=0;//答案可能为c(n,2),会爆int
for(int i=3,cnt=0;i<=n;i++){
    if(s[i-2]==s[n]/3)cnt++;
    if(s[n]-s[i-1]==s[n]/3)res+=cnt;
}

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值