差分算法 cpp


5.差分

优化暴力枚举

前缀和与差分的核心思想都是预处理, 可以在暴力枚举中快速给出查询结果, 从而优化时间复杂度

经典的用空间换时间的做法

前缀和 和 差分 是一对互逆运算

5.1 一维差分

!牛客网

【模板】差分

思路:

利用差分数组解决问题

差分数组的作用: 快速解决"将某一个区间所有元素统一加上一个数" 的操作

O(1)

  1. 预处理差分数组
    f[i]表示: 当前元素与以前一个元素的差值
  2. 利于差分数组解决m次修改操作
    差分数组的性质: 原数组[L, R]区间全部加k这个操作 = 在差分数组中f[L] += k; f[R+1] -= k;
  3. 差分数组 -> 原数组(还原)
    a[i] = f[i] + a[i-1];

注意事项:

差分数组使用时, 所有操作必须全部进行完后, 才能还原出操作之后的数组(差分数组使用条件)

代码:

暴力解法 -> 枚举

cpp 复制代码
#include<bits/stdc++.h>

using namespace std;

const int N  = 1e5 + 10;
typedef long long LL;

LL a[N];
LL n, m;
LL l, r, k;

int main()
{
	cin >> n >> m;
	
	for(int i = 1; i<= n; i++) cin >> a[i];
	
	while(m--){
		cin >> l >> r >> k;
		
		for(int i = l; i <= r; i++) a[i]+=k;
	}
	
	for(int i = 1; i<= n; i++) cout << a[i] << " ";
		
	return 0;
}

部分用例会超时

差分数组解决:

cpp 复制代码
#include<bits/stdc++.h>

using namespace std;

const int N  = 1e5 + 10;
typedef long long LL;

LL a[N];
LL n, m;
LL l, r, k;
LL f[N];

int main()
{
	cin >> n >> m;
	
	for(int i = 1; i<= n; i++) {
		cin >> a[i];
		
		//构造差分数组
		f[i] = a[i] - a[i-1]; 
	}
	

	while(m--){
		cin >> l >> r >> k;
		
		//利用差分数组的性质进行操作 
		f[l] += k;
		f[r+1] -= k; 
	}
	
	 
	for(int i = 1; i<= n; i++){	
		a[i] = f[i] + a[i-1];
		cout << a[i] << " ";
	} 
		
	return 0;
}

5.2 海底高铁

!洛谷

P3406 海底高铁

P3406 海底高铁 - 洛谷

题目描述

该铁路经过 N N N 个城市,每个城市都有一个站。不过,由于各个城市之间不能协调好,于是乘车每经过两个相邻的城市之间(方向不限),必须单独购买这一小段的车票。第 i i i 段铁路连接了城市 i i i 和城市 i + 1 ( 1 ≤ i < N ) i+1(1\leq i<N) i+1(1≤i<N)。如果搭乘的比较远,需要购买多张车票。第 i i i 段铁路购买纸质单程票需要 A i A_i Ai 博艾元。

虽然一些事情没有协调好,各段铁路公司也为了方便乘客,推出了 IC 卡。对于第 i i i 段铁路,需要花 C i C_i Ci 博艾元的工本费购买一张 IC 卡,然后乘坐这段铁路一次就只要扣 B i ( B i < A i ) B_i(B_i<A_i) Bi(Bi<Ai) 元。IC 卡可以提前购买,有钱就可以从网上买得到,而不需要亲自去对应的城市购买。工本费不能退,也不能购买车票。每张卡都可以充值任意数额。对于第 i i i 段铁路的 IC 卡,无法乘坐别的铁路的车。

Uim 现在需要出差,要去 M M M 个城市,从城市 P 1 P_1 P1 出发分别按照 P 1 , P 2 , P 3 , ⋯   , P M P_1,P_2,P_3,\cdots,P_M P1,P2,P3,⋯,PM 的顺序访问各个城市,可能会多次访问一个城市,且相邻访问的城市位置不一定相邻,而且不会是同一个城市。

现在他希望知道,出差结束后,至少会花掉多少的钱,包括购买纸质车票、买卡和充值的总费用。

输入格式

第一行两个整数, N , M N,M N,M。

接下来一行, M M M 个数字,表示 P i P_i Pi。

接下来 N − 1 N-1 N−1 行,表示第 i i i 段铁路的 A i , B i , C i A_i,B_i,C_i Ai,Bi,Ci。

输出格式

一个整数,表示最少花费

输入输出样例 #1

输入 #1

复制代码
9 10
3 1 4 1 5 9 2 6 5 3
200 100 50
300 299 100
500 200 500
345 234 123
100 50 100
600 100 1
450 400 80
2 1 10

输出 #1

复制代码
6394

说明/提示

2 2 2 到 3 3 3 以及 8 8 8 到 9 9 9 买票,其余买卡。

对于 30 % 30\% 30% 数据 M = 2 M=2 M=2。

对于另外 30 % 30\% 30% 数据 N ≤ 1000 , M ≤ 1000 N\leq1000,M\leq1000 N≤1000,M≤1000。

对于 100 % 100\% 100% 的数据 M , N ≤ 10 5 , A i , B i , C i ≤ 10 5 M,N\leq 10^5,A_i,B_i,C_i\le10^5 M,N≤105,Ai,Bi,Ci≤105。

思路:
  1. 先知道每一段高铁被乘坐了多少次, 记作num[i]
  2. 最小花费 = 买票花费与(买卡: 乘车+工本费)的最小值
    A / B C
    mincost = min(a[i] * num[i], b[i]* num[i] + c[i]);
  3. 每一次访问从一地到另一地都会经过中间所有铁路, 即 [ p i , p i + 1 − 1 ] [p_i,p_i+1 -1] [pi,pi+1−1]
    1. 创建一个全为0的差分数组f
    2. 遍历访问序列 , 每次访问 : f [ p i ] + + , f [ p i + 1 ] − − f[p_i]++, f[p_i+1]-- f[pi]++,f[pi+1]−−
    3. 然后对差分数组做一次前缀和, 就能得到每个高铁乘坐的次数
  4. 注意, 城市访问序列有可能是从大序到小序, 此时交换数据即可
代码:
cpp 复制代码
#include<bits/stdc++.h>

using namespace std;

const int N = 1e5 + 10;
typedef long long LL;

int n, m;

LL f[N];

int main()
{
	cin >> n >> m;
	
	//x->y
	int x;
	cin >> x;
	for(int i = 2; i <= m; i++ ){
		int y;
		cin >> y;
		
		//x->y
		if(x > y){
			f[y]++;
			f[x]--;
		}else{
			f[x]++;
			f[y]--;
		}
		x = y;
	} 
	
	//利用差分数组, 还原出数组
	for(int i = 1; i <= n; i++)f[i] += f[i-1];
	
	//直接求结果
	LL ret = 0;
	for(int i = 1; i < n ; i++){
		LL a, b, c;
		cin >> a >> b >> c;
		ret += min(a*f[i], b*f[i] + c);
	} 
	
	cout << ret;

	return 0;
}

相关推荐
Bczheng117 小时前
五.serialize.h中的CDataStream类
算法·哈希算法
小O的算法实验室18 小时前
2025年SEVC,考虑组件共享的装配混合流水车间批量流调度的多策略自适应差分进化算法,深度解析+性能实测
算法·论文复现·智能算法·智能算法改进
汀、人工智能18 小时前
[特殊字符] 第36课:柱状图最大矩形
数据结构·算法·数据库架构·图论·bfs·柱状图最大矩形
List<String> error_P18 小时前
蓝桥杯最后冲刺(三)
算法
样例过了就是过了18 小时前
LeetCode热题100 跳跃游戏
c++·算法·leetcode·贪心算法·动态规划
chen_ever18 小时前
从网络基础到吃透 Linux 高并发 I/O 核心(epoll+零拷贝 完整版)
linux·网络·c++·后端
无限进步_18 小时前
【C++&string】寻找字符串中第一个唯一字符:两种经典解法详解
开发语言·c++·git·算法·github·哈希算法·visual studio
FluxMelodySun18 小时前
机器学习(二十九) 稀疏表示与字典学习(LASSO算法、KSVD算法、奇异值分解)
人工智能·算法·机器学习
小此方18 小时前
Re:思考·重建·记录 现代C++ C++11篇 (二) 右值引用与移动语义&引用折叠与完美转发
开发语言·c++·c++11·现代c++
LG.YDX18 小时前
笔试训练48天:跳台阶
数据结构·算法