随笔 - 21  文章 - 0  评论 - 0  阅读 - 3274 

  前缀和与差分是最基础的算法,也是最简单的算法之一。不过它们是降低时间复杂度的有力算法,利用前缀和或差分进行预处理,往往可以将时间复杂度降低一个n的规模。给定一个数组a[n],设第i个前缀和为s[i],第i个差分为d[i],s[i]就是从第1个元素到第i个元素的数的总和,d[i]就是a[i]-a[i-1]。s[i]=s[i-1]+a[i]。我们容易看出,前缀和与差分互为逆过程。那么,它们有什么用处呢?假如说我们要求一段区间[l,r]之和,按照朴素算法思想,我们需要从区间首加到区间尾部,如果要求m次区间和,复杂度高达O(mn),这显然无法接受,于是我们可以使用前缀和,以O(n)的复杂度预处理出前缀和,之后每次询问只要输出s[r]-s[l-1]即可,时间复杂度降低至O(m+n),可以接受。差分的运用更有意思,假设我们每次对一段区间[l,r]上的所有数据同时进行加减操作,例如同时加上x,求m次的操作之后数组每个的值是多少。按照朴素算法思想,直接对题意进行模拟操作,时间复杂度高达O(mn),无法接受。于是我们想到了差分,我们定义一个差分数组d[n],其中d[i]=a[i]-a[i-1],进行操作时,只需要d[l]+=x,d[r+1]-=x即可,这样可以直接表示出这个操作,最后对差分数组d[i]求前缀和即可得到答案。

  先来一道前缀和的例题。

  

题目描述

小 K 打下的江山一共有n个城市,城市ii和城市i+1有一条双向高速公路连接,走这条路要耗费时间ai

小 K 为了关心人民生活,决定定期进行走访。他每一次会从1号城市到n号城市并在经过的城市进行访问。其中终点必须为城市n

不仅如此,他还有一个传送器,传送半径为k,也就是可以传送到i-ki+k。如果目标城市编号小于1则为1,大于n则为n

但是他的传送器电量不足,只能传送一次,况且由于一些原因,他想尽量快的完成访问,于是就想问交通部部长您最快的时间是多少。

注意:他可以不访问所有的城市,使用传送器不耗费时间

输入格式

两行,第一行n,k

第二行n-1个整数,第i个表示ai

输出格式

一个整数,表示答案。

输入输出样例

输入 #1

4 0

1 2 3

输出 #1

6

  总之就是非常简单,找到区间长度为k的区间中区间和最大的,再用总和减去它,就得到了答案。

  代码如下

复制代码
#include<iostream>
#include<cstdio>
typedef long long ll;
const int maxn=(1e6)+5;
using namespace std;
int n,k;
ll a[maxn];
ll sum[maxn];
int main(){
    scanf("%d %d",&n,&k);
    for(int i=1;i<n;i++){
        scanf("%lld",&a[i]);
        sum[i]=sum[i-1]+a[i];
    }
    ll t=0;
    for(int i=0;i<=n-1-k;i++){
        t=max(t,sum[i+k]-sum[i]);
    }
    ll ans=sum[n-1]-t;
    if(k>=n-1)ans=0;
    printf("%lld\n",ans);
    return 0;
}
复制代码

再来一道差分的例题

Color the ball

Time Limit: 9000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 45070    Accepted Submission(s): 20817

Problem Description
N个气球排成一排,从左到右依次编号为1,2,3....N.每次给定2个整数a b(a <= b),lele便为骑上他的“小飞鸽"牌电动车从气球a开始到气球b依次给每个气球涂一次颜色。但是N次以后lele已经忘记了第I个气球已经涂过几次颜色了,你能帮他算出每个气球被涂过几次颜色吗?
Input
每个测试实例第一行为一个整数N,(N <= 100000).接下来的N行,每行包括2个整数a b(1 <= a <= b <= N)。
当N = 0,输入结束。
Output
每个测试实例输出一行,包括N个整数,第I个数代表第I个气球总共被涂色的次数。
Sample Input
3 1 1 2 2 3 3 3 1 1 1 2 1 3 0
Sample Output
1 1 1 3 2 1
 
  差分的模板题,不用多说,就是前面所说的套路。
  以下为代码
复制代码
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=100005;
int d[maxn],a[maxn];
int main(){
    int n;
    scanf("%d",&n);
    while(n){
        memset(a,0,sizeof(a));
        memset(d,0,sizeof(d));
        int x,y;
        int m=n;
        while(m--){
            scanf("%d%d",&x,&y);
            d[x]+=1;
            d[y+1]-=1;
        }
        for(int i=1;i<n;i++){
            a[i]=a[i-1]+d[i];
            printf("%d ",a[i]);
        }
        a[n]=a[n-1]+d[n];
        printf("%d\n",a[n]);//卡格式
        scanf("%d",&n);
    }
    return 0;
}
复制代码

 

posted on   雪之下雪乃天下第一  阅读(142)  评论(0编辑  收藏  举报
编辑推荐:
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
点击右上角即可分享
微信分享提示