基本算法之前缀和与差分的是使用

本文介绍了如何使用前缀和与差分技术解决编程竞赛中的二维区域查询和区间修改问题。通过实例解析了激光炸弹、区间修改等经典算法题目的解题思路,并提供了相应的代码实现。此外,还探讨了增减序列的优化策略,以达到最小操作次数。文章深入浅出地阐述了这些技术在解决实际问题中的应用。
摘要由CSDN通过智能技术生成

前缀和

鸣谢

添加链接描述

添加链接描述

二维前缀和

在这里插入图片描述

在这里插入图片描述

激光炸弹

题目链接

解题思路:

解法思路:我们首先观察题目,可以建立一个数组f[i][j]表示坐标为(xi,yj)(xi,yj)上的权值,那么我们接着思考,因为题目上面说了要求算出这个边长为r的正方形面积,而且整个题目中,只有查询操作,没有修改操作,且内存只要略微省着用,就可以满足O(n2)O(n2)的条件,所以我们可以确认这一题可以使用二维前缀和。

代码:

#include<stdio.h>
#include<algorithm>
using namespace std;
int n,r;
int g[5010][5010];

int main(){
    scanf("%d%d",&n,&r);
    r = min(5001,r); // 变了这里
    for(int i=1;i<=n;++i){
        int x,y,w;
        scanf("%d%d%d",&x,&y,&w);
        x++,y++;
        g[x][y] +=  w;
    }
    for(int i=1;i<=5001;++i)
        for(int j=1;j<=5001;++j)
            g[i][j] +=  g[i-1][j] + g[i][j-1] - g[i-1][j-1];
    int res = 0;
   for(int i=r;i<=5001;++i)
        for(int j=r;j<=5001;++j) 
            res = max(res,g[i][j] - g[i][j-r] - g[i-r][j] + g[i-r][j-r]);
    printf("%d\n",res);
    return 0;
}

差分

求差分

求出a 的差分序列 b , b 1 = a 1 , b i = a i − a i − 1 ( 2 < = i < = n ) b_1 = a_1,b_i = a_i - a_{i-1} (2 <= i <= n) b1=a1,bi=aiai1(2<=i<=n)

代码:

// a 从下标一开始存
// 如果用原来的 数组求差分序列,要从后向前 求,因为要保留 a[i-1]
    for(int i = n ; i>1;--i)
        a[i] = a[i] - a[i-1];

差分求区间修改

把区间a[l ,r] 都加上一个 数 d;
b [ l ] + = d , b [ r + 1 ] + = d b[l] += d , b[r+1] += d b[l]+=d,b[r+1]+=d

把区间a[l ,r] 都减去一个 数 d;
b [ l ] − = d , b [ r + 1 ] + = d b[l] -= d , b[r+1] += d b[l]=d,b[r+1]+=d

增减序列

题目链接

解题思路:

首先明确 将所有的数一样大,那么从2~n的差分序列都为0,b[1] 可以不为0.都与b[1] 一样即可。在操作的时候我们采取贪心的思路,因为每一次操作都是一正一负,至于为什么要是正负配对,因为我们是要这个B序列2~n都要为0,所以这样负数增加,正数减少,就可以最快地到达目标为0的状态。
至于那些无法配对的数BkBk可以选B1B1或者Bn+1Bn+1,这两个不影响的数,进行修改。

因此最小操作次数为:
m i n ( p o s , n e g ) + a b s ( p o s − n e g ) = m a x ( p o s , n e g ) min(pos,neg) + abs(pos-neg) = max(pos,neg) min(pos,neg)+abs(posneg)=max(pos,neg)
种类数:
a b s ( p o s − n e g ) + 1 abs(pos -neg)+1 abs(posneg)+1

代码:

#include<stdio.h>
#include<math.h>
#include<algorithm>
using namespace std;

const int N = 1e5 + 10;
typedef long long ll;
int n;
ll a[N];
ll psum,nsum;
int main(){
    scanf("%d",&n);
    psum = nsum = 0;
    for(int i = 1;i<=n;++i)
        scanf("%lld",&a[i]);
        
    for(int i = n ; i>1;--i)
        a[i] = a[i] - a[i-1];
    for(int i = 2;i<= n ;++i)
        if(a[i] > 0)
            psum += a[i];
        else 
            nsum -= a[i]; // 加负数的绝对值,那么直接减就好
    printf("%lld\n%lld\n",max(psum,nsum),abs(psum - nsum) + 1);
    return 0;
}

最高的牛

题目链接

解题思路:
在这里插入图片描述
代码:

#include<stdio.h>
#include<stdlib.h>
#include<map>

using namespace std;

const int N = 1e4 + 10;
// 用map 和 pair 来记录是否重复
map <pair<int ,int>,bool> vis;
int a[N],b[N];
int n,p,h,m;
int x,y;
int main(){
    scanf("%d%d%d%d",&n,&p,&h,&m);
    for(int i=1;i<= m ;++i){
        scanf("%d%d",&x,&y);
        if(x>y)swap(x,y);
        if(vis[make_pair(x,y)])continue;
        b[x+1]--  , b[y]++ ; // 所以不能 x++
        vis[make_pair(x,y)] = 1; // 因为这里还要用到 x 
    }
    for(int i=1;i<=n;++i){
        a[i] = a[i - 1] + b[i];
        printf("%d\n",h+a[i]);
    }
    return  0;
    
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

落春只在无意间

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

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

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

打赏作者

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

抵扣说明:

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

余额充值