算法竞赛指南———打卡 0x03 前缀和与差分

先说下基本知识

  1. 前缀和
    一维前缀和
    如果我给你一串长度为n的数列a1,a2,a3…an,再给出m个询问,每次询问给出L,R两个数,要求给出区间[L,R]里的数的和,你会怎么做,若是没有了解过前缀和的人看到这道题的想法可能是对于m次询问,我每次都遍历一遍它给的区间,计算出答案,这样子的方法固然没错,但是其时间复杂度达到了O(n*m),如果数据量稍微大一点就有可能超时,而我们如果使用前缀和的方法来做的话就能够将时间复杂度降到O(n+m),大大节省了运算时间。至于怎么用,请看下面一小段代码

    for(int i=1;i<=n;i++)
      a[i]+=a[i-1];
    

    前缀和顾名思义就是前面i个数的总和。数组a在经过这样的操作之后,对于每次的询问,我们只需 要计算a[R]-a[L-1]就能得到我们想要的答案了,是不是很简单呢。

  2. 差分
    没错,前缀和顾名思义就是前面i个数的总和。数组a在经过这样的操作之后,对于每次的询问,我们只需要计算a[R]-a[L-1]就能得到我们想要的答案了,是不是很简单呢。

    在知道了最简单的前缀和之后,我们再来了解一下什么是差分。

    给你一串长度为n的数列a1,a2,a3…an,要求对a[L]~a[R]进行m次操作:

    操作一:将a[L]~a[R]内的元素都加上P

    操作二:将a[L]~a[R]内的元素都减去P

    最后再给出一个询问求a[L]-a[R]内的元素之和?

    你会怎么做呢?你可能会想,我对于m次操作每次都遍历一遍a[L]~a[R],给区间里的数都加上P或减去P,最后再求一次前缀和就行了。没错,这样子确实也能得出正确答案,但时间复杂度却高达O(M*n),对于1<=n,m<=1e5这个数据范围来说直接就tle了,所以说这个方法不可行。既然这样不行的话,那我们要怎么做才能快速的得到正确答案呢?是的,这个时候我们的差分就该派上用场了,我们新开一个数组b,储存每一次的修改操作,最后求前缀和的时候统计一下就能快速的得到正确答案了,详细请看下面代码。

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=1e5+9;
    int a[maxn],b[maxn];
    int main(){
    int i,j,k,n,m,p;
    cin>>n>>m;
    for(i=1;i<=n;i++){
    	cin>>a[i];
    }
    for(i=1;i<=m;i++){
    	int L,R,t;
    	cin>>t>>L>>R>>p;
    	if(t==1){
    		b[L]+=p;b[R+1]-=p; //仔细想想为什么b[R+1]要减去p 
    	}
    	else{
    		b[L]-=p;b[R+1]+=p;
    	}
    }
    int add=0;
    for(i=1;i<=n;i++){
    	add+=b[i];
    	a[i]+=a[i-1]+add;
    }
    int x,y;
    cin>>x>>y;
    cout<<a[y]-a[x-1]<<endl;
    }
    

相信看到这里,大家已经仔细思考过代码了,为什么操作一时b[R+1]要减去p,很简单,因为操作一我只需对[L,R]区间里的数加p,[R+1,n]这个区间里的数没必要加p,所以需要减掉p。

原文链接:https://blog.csdn.net/K_R_forever/article/details/81775899

一张图理解差分数组

前人之述备矣,那就做些简单练习吧
简单例题:
1.一维数组 的前缀和 sum(l,r) = ∑(l~r) A[r]-A[l-1]
二维数组可以类似求出
链接:https://ac.nowcoder.com/acm/contest/999/A
来源:牛客网
一种新型的激光炸弹,可以摧毁一个边长为R的正方形内的所有的目标。
现在地图上有n(N ≤ 10000)个目标,用整数Xi,Yi(其值在[0,5000])表示目标在地图上的位置,每个目标都有一个价值。
激光炸弹的投放是通过卫星定位的,但其有一个缺点,就是其爆破范围,即那个边长为R的正方形的边必须和x,y轴平行。
若目标位于爆破正方形的边上,该目标将不会被摧毁。
输入描述:
输入文件的第一行为正整数n和正整数R,接下来的n行每行有3个正整数,分别表示 xi,yi ,vi 。
输出描述:
输出文件仅有一个正整数,表示一颗炸弹最多能炸掉地图上总价值为多少的目标(结果不会超过32767)。
示例1
输入
复制
2 1
0 0 1
1 1 1
输出
复制
1

#include<iostream>
using namespace std;
#define Max 5010
int g[Max][Max];
int N,r,ans;

int main(){
    cin>>N>>r;
    int x,y,v,m=r,n=r;
   for(int i=0;i<N;i++)
       {
           cin>>x>>y>>v;
           x++,y++;//避免边界问题
           n = max(n,x)           ;
           m = max(m,y);
           g[x][y]+=v;
       }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            g[i][j]+=g[i-1][j]+g[i][j-1]-g[i-1][j-1];
    for(int i=r;i<=n;i++)
        for(int j=r;j<=m;j++)
            ans = max(ans,g[i][j]-g[i-r][j]-g[i][j-r]+g[i-r][j-r]);
    cout<<ans<<endl;
    return 0;
}

2.IncDec Sequnce
链接:https://ac.nowcoder.com/acm/contest/999/B
来源:牛客网
给定一个长度为 n(n≤105) 的数列a1​,a2​,…,an​,每次可以选择一个区间 [l,r],使下标在这个区间内的数都加一或者都减一。
求至少需要多少次操作才能使数列中的所有数都一样,并求出在保证最少次数的前提下,最终得到的数列可能有多少种。
输入描述:
第一行一个正整数n。
接下来n行,每行一个整数,第i+1行的整数表示ai​。
输出描述:
第一行输出最少操作次数。
第二行输出最终能得到多少种结果。
示例1
输入
复制
4
1
1
2
2
输出
复制
1
2
备注:
对于100%的数据,n=100000,0≤ai<2147483648

#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;
int a[100005];
int b[100005];
int main()
{
	int n;
	ios_base::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	cin >> n;
	cin >> a[0];
	b[0] = a[0];
	long long x = 0, y = 0;
	for (int i = 1; i < n; i++)
	{
		cin >> a[i];
		b[i] = a[i] - a[i - 1];//差分
	}
	for (int i = 0; i < n; i++)
	{
		if (b[i] >= 0)x += b[i];
		else y -= b[i];
	}
	cout << max(x, y) << '\n' << abs(x - y) + 1;
	system("pause");
	return 0;
}

3.链接:https://ac.nowcoder.com/acm/contest/999/C
来源:牛客网
FJ’s N (1 ≤ N ≤ 10,000) cows conveniently indexed 1…N are standing in a line. Each cow has a positive integer height (which is a bit of secret). You are told only the height H (1 ≤ H ≤ 1,000,000) of the tallest cow along with the index I of that cow.
FJ has made a list of R (0 ≤ R ≤ 10,000) lines of the form “cow 17 sees cow 34”. This means that cow 34 is at least as tall as cow 17, and that every cow between 17 and 34 has a height that is strictly smaller than that of cow 17.
For each cow from 1…N, determine its maximum possible height, such that all of the information given is still correct. It is guaranteed that it is possible to satisfy all the constraints.
输入描述:
Line 1: Four space-separated integers: N, I, H and R
Lines 2…R+1: Two distinct space-separated integers A and B (1 ≤ A, B ≤ N), indicating that cow A can see cow B.
输出描述:
Lines 1…N: Line i contains the maximum possible height of cow i.
示例1
输入
复制
9 3 5 5
1 3
5 3
4 3
3 7
9 8
输出
复制
5
4
5
3
4
4
5
5
5

#include <iostream>
#include <map>
#include <cstring>
using namespace std;

map<pair<int, int>, bool>existed;

int c[10010], d[10010];

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	int n, m, p, h;
	cin >> n >> p >> h >> m;
	for (int i = 1; i <= m; i++)
	{
		int a, b;
		cin >> a >> b;
		if (a > b)swap(a, b);
		if (existed[make_pair(a, b)]) continue;
		d[a + 1]--, d[b]++;//a~b之间的数都减1
		existed[make_pair(a, b)] = true;
	}
	for (int i = 1; i<=n; i++)
	{
		c[i] = c[i - 1] + d[i];//差分
		cout << c[i] + h << endl;
	}
	system("pause");
	return 0;
}

若有错误,还望指正。谢谢
如果你觉得文字有些枯燥还可以选择更生动的方式理解

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值