算法复盘——差分

一、思路

一维差分:

核心作用

区间 [l, r] 加 c 这种操作,从 O (n) 降到 O(1)

核心公式

  • 差分数组:diff[i] = a[i] - a[i-1]
  • 区间加:diff[l] += cdiff[r+1] -= c
  • 还原原数组:求前缀和

二维数组:

核心作用

子矩阵矩形区域统一加 c,O (1) 完成。

核心公式

复制代码
diff[x1][y1] += c
diff[x1][y2+1] -= c
diff[x2+1][y1] -= c
diff[x2+1][y2+1] += c

最后用二维前缀和还原。

二、例题

1.语文成绩

P2367 语文成绩 - 洛谷

思路:

  • 初始有 n 个学生成绩,需要执行 p 次「给第 x 到第 y 个学生每人加 z 分」的操作。
  • 最后要输出全班的最低分

分析:

题目的要求即区间修改 和 单点查询,可利用差分数组来处理区间修改,然后使用前缀和还原原数组,查找最小值。

代码实现:

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;

#define int long long
const int N = 5e6 + 10; 
int n, p, g[N], f[N], gmin = 100;
signed main()
{
	cin >> n >> p;
	for(int i = 1; i <= n; i++)
	{
		cin >> g[i];
		f[i] = g[i] - g[i-1];
	}
	while(p--)
	{
		int l, r, x;
		cin >> l >> r >> x;
		f[l] += x, f[r+1] -= x;
	}
	for(int i = 1; i <= n; i++)
	{
		g[i] = g[i-1] + f[i];
		gmin = min(gmin, g[i]);
	}
	cout << gmin << endl;
	return 0;
}

2.海底高铁

P3406 海底高铁 - 洛谷

思路:

依次经过若干城市,每段相邻城市之间的铁路会被多次经过 ,我们需要先统计出每段铁路被经过多少次

分析:

从城市 x 走到 y,等价于对区间 [x, y−1] 的铁路都经过一次。

代码实现:

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;

#define int long long
const int N = 1e5 + 10;
int n, m, f[N], a, b, c, ret;
int x, y;
signed main ()
{
	cin >> n >> m;
	cin >> x;
	for(int i = 2; i <= m; i++)
	{
		cin >> y;
		if(x < y) f[x]++, f[y]--;
		if(x > y) f[y]++, f[x]--;
		x = y;
	}
	for(int i = 1; i <= n; i++) f[i] += f[i-1];
	for(int i = 1; i < n; i++)
	{
		cin >> a >> b >> c;
		ret += min(a * f[i], c + f[i] * b);
	}
	cout << ret << endl;
	return 0;
}

3.借教室

P1083 [NOIP 2012 提高组] 借教室 - 洛谷

思路:

按顺序处理若干借教室订单,每个订单需要在第 s 天到第 t 天每天借用 d 间教室。若某一天所需教室超过现有数量,则该订单无法满足,要求找到第一个无法满足的订单编号

分析:

订单越多,每天的教室需求只会增加不会减少,因此满足情况呈现单调性:前 k 个可满足 → 所有更小编号都可满足;前 k 个不可满足 → 所有更大编号都不可满足。可用二分答案定位第一个不满足的订单。

对二分出的前 mid 个订单,用差分快速统计每天教室总需求,前缀和还原得到每天实际需求,判断是否存在某天超过可用教室数。

代码实现:

cpp 复制代码
// 暴力
#define int long long 
const int N = 1e6 + 10;
int n, m, r[N], f[N]; 

signed main()
{
	cin >> n >> m;
	for (int i = 1; i <= n; i++) cin >> r[i];
	// 初始化差分数组
	for (int i = 1; i <= n; i++) f[i] = r[i] - r[i-1];
	f[n+1] = -r[n];
	
	for (int k = 1; k <= m; k++) {
		int d, s, t;
		cin >> d >> s >> t;
		// 差分标记这个订单
		f[s] -= d;
		f[t+1] += d;
		
		// 前缀和检查是否有天不够
		int now = 0;
		bool ok = true;
		for (int i = 1; i <= n; i++) {
			now += f[i];
			if (now < 0) {
				cout << -1 << endl << k << endl;
				return 0;
			}
		}
	}
	cout << 0 << endl;
	return 0;
}
cpp 复制代码
#include <bits/stdc++.h>
using namespace std;

const int N = 1e6 + 10;
int n, m;
int r[N], d[N], s[N], t[N];
long long diff[N];  // 用long long防止溢出

// 检查前k个订单是否可行
bool check(int k) {
    memset(diff, 0, sizeof(diff));
    // 差分标记前k个订单
    for (int i = 1; i <= k; i++) {
        diff[s[i]] += d[i];
        diff[t[i] + 1] -= d[i];
    }
    // 前缀和还原,判断是否超量
    long long now = 0;
    for (int i = 1; i <= n; i++) {
        now += diff[i];
        if (now > r[i]) return false;
    }
    return true;
}

signed main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    
    cin >> n >> m;
    for (int i = 1; i <= n; i++) cin >> r[i];
    for (int i = 1; i <= m; i++) cin >> d[i] >> s[i] >> t[i];
    
    // 二分找第一个不可行的订单
    int l = 1, r = m, ans = 0;
    while (l <= r) {
        int mid = (l + r) >> 1;
        if (check(mid)) {
            ans = mid;
            l = mid + 1;
        } else {
            r = mid - 1;
        }
    }
    
    if (ans == m) cout << 0 << endl;
    else {
        cout << -1 << endl;
        cout << ans + 1 << endl;
    }
    return 0;
}

4.地毯

P3397 地毯 - 洛谷

代码实现:

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;


const int N = 1010;
int f[N][N];

int main ()
{
	int m, n; cin >> n >> m;
	
	while(m--)
	{
		int x1, x2, y1, y2; cin >> x1 >> y1 >> x2 >> y2;
		f[x1][y1] ++;
		f[x1][y2 + 1] --;
		f[x2 + 1][y1] --;
		f[x2 + 1][y2 + 1] ++;
	}
	
	for (int i = 1; i <= n; i++)
	{
		for (int j = 1; j <= n; j++)
		{
			f[i][j] += f[i-1][j] + f[i][j-1] - f[i-1][j-1];
			cout << f[i][j] << " ";
		}
		cout << endl;
	}
	
	return 0;
}
相关推荐
qq_398586544 小时前
平衡三进制超前进位加法器
算法
第二层皮-合肥4 小时前
50天学习FPGA第32天-添加HDL属性调试
学习·fpga开发
西西弟4 小时前
最短路径之Dijkstra算法(数据结构)
数据结构·算法
沉鱼.444 小时前
树形DP题目
算法·深度优先
_李小白4 小时前
【OSG学习笔记】Day 23: ClipNode(动态裁剪)
android·笔记·学习
VelinX4 小时前
【个人学习||算法】多维动态规划
学习·算法·动态规划
AlenTech4 小时前
139. 单词拆分 - 力扣(LeetCode)
算法·leetcode·职场和发展
老鱼说AI4 小时前
大模型学习与面试精讲第六期:损失函数篇
人工智能·深度学习·神经网络·学习·机器学习·语言模型
郭涤生4 小时前
std::async 和 std::future的使用
c++