差分(模板)

语文成绩

题目背景

语文考试结束了,成绩还是一如既往地有问题。

题目描述

语文老师总是写错成绩,所以当她修改成绩的时候,总是累得不行。她总是要一遍遍地给某些同学增加分数,又要注意最低分是多少。你能帮帮她吗?

输入格式

第一行有两个整数 n n n p p p,代表学生数与增加分数的次数。

第二行有 n n n 个数, a 1 ∼ a n a_1 \sim a_n a1an,代表各个学生的初始成绩。

接下来 p p p 行,每行有三个数, x x x y y y z z z,代表给第 x x x 个到第 y y y 个学生每人增加 z z z 分。

输出格式

输出仅一行,代表更改分数后,全班的最低分。

样例 #1

样例输入 #1

3 2
1 1 1
1 2 1
2 3 1

样例输出 #1

2

提示

对于 40 % 40\% 40% 的数据,有 n ≤ 1 0 3 n \le 10^3 n103

对于 60 % 60\% 60% 的数据,有 n ≤ 1 0 4 n \le 10^4 n104

对于 80 % 80\% 80% 的数据,有 n ≤ 1 0 5 n \le 10^5 n105

对于 100 % 100\% 100% 的数据,有 n ≤ 5 × 1 0 6 n \le 5\times 10^6 n5×106 p ≤ n p \le n pn,学生初始成绩 $ \le 100 , , z \le 100$。


一、考虑暴力算法:

最坏情况下时间复杂度是 O ( n p ) O(np) O(np),绝对超时。


二、差分算法
定义

定义差分数组为 b [ ] b[] b[] ,原数组为 a [ ] a[] a[]

定义 b [ i ] = a [ i ] − a [ i − 1 ] b[i] = a[i]-a[i-1] b[i]=a[i]a[i1]

区间修改: 时间复杂度为 O ( 1 ) O(1) O(1)

在这区间 [ l , r ] [l,r] [l,r] 中间的 b [ i ] b[i] b[i],

b [ i ] = ( a [ i ] + x ) − ( a [ i − 1 ] + x ) = a [ i ] − a [ i − 1 ] b[i] = (a[i]+x)-(a[i-1]+x) = a[i]-a[i-1] b[i]=(a[i]+x)(a[i1]+x)=a[i]a[i1] 无需改动

只有左右两端 b [ l ] b[l] b[l] b [ r + 1 ] b[r+1] b[r+1] 需要改变

b [ l ] = ( a [ l ] + x ) − a [ l − 1 ] = a [ l ] − a [ l − 1 ] + x b[l] = (a[l]+x)-a[l-1] = a[l]-a[l-1]+x b[l]=(a[l]+x)a[l1]=a[l]a[l1]+x 所以 b [ l ] b[l] b[l] 需要 + = x +=x +=x

b [ r + 1 ] = a [ r + 1 ] − ( a [ r ] + x ) = a [ r + 1 ] − a [ r ] − x b[r+1] = a[r+1]-(a[r]+x) = a[r+1]-a[r]-x b[r+1]=a[r+1](a[r]+x)=a[r+1]a[r]x 所以 b [ r + 1 ] b[r+1] b[r+1]需要 − = x -=x =x

查询:复杂度 O ( n ) O(n) O(n)

最后需要查询全班的最低分,所以我们需要用 b [ i ] b[i] b[i] 推出 a [ i ] a[i] a[i]

推导过程如下:

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

a [ i ] = b [ i ] + a [ i − 1 ] a[i] = b[i]+a[i-1] a[i]=b[i]+a[i1]

a [ i ] = b [ i ] + b [ i − 1 ] + a [ i − 2 ] a[i] = b[i]+b[i-1]+a[i-2] a[i]=b[i]+b[i1]+a[i2]

a [ i ] = b [ i ] + b [ i − 1 ] + b [ i − 2 ] + a [ i − 3 ] a[i] = b[i]+b[i-1]+b[i-2]+a[i-3] a[i]=b[i]+b[i1]+b[i2]+a[i3]

a [ i ] = b [ i ] + b [ i − 1 ] + b [ i − 2 ] + b [ i − 3 ] + a [ i − 4 ] a[i] = b[i]+b[i-1]+b[i-2]+b[i-3]+a[i-4] a[i]=b[i]+b[i1]+b[i2]+b[i3]+a[i4]

… … …… ……

a [ i ] = b [ i ] + b [ i − 1 ] + b [ i − 2 ] + … … + b [ 1 ] a[i]=b[i]+b[i-1]+b[i-2]+……+b[1] a[i]=b[i]+b[i1]+b[i2]+……+b[1]

a [ i ] a[i] a[i] 即为 b [ 1 ] + b [ 2 ] + … … + b [ i ] b[1]+b[2]+……+b[i] b[1]+b[2]+……+b[i]

所以 a [ ] a[] a[] b [ ] b[] b[] 的前缀和数组

利用这些,我们可以写出代码

完整AC代码:
#include<iostream>
using namespace std;
int a[5000005], b[5000005];
int main()
{
	ios::sync_with_stdio(false); 
	cin.tie(0), cout.tie(0);
	int n, p;
	cin >> n >> p;
	for(int i=1;i<=n;i++)
	{
		cin >> a[i];
		b[i] = a[i]-a[i-1];
	}
	for(int i=1;i<=p;i++)
	{
		int x, y, z;
		cin >> x >> y >> z;
		b[x] += z;
		b[y+1] -= z;
	}
	int ans = 100000000;
	for(int i=1;i<=n;i++)
	{
		a[i] = a[i-1]+b[i];
		ans = min(ans, a[i]);
	}
	cout << ans;
	return 0;
}

AC记录

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值