算法复盘——差分

一、思路

一维差分:

核心作用

区间 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;
}
相关推荐
来两个炸鸡腿9 小时前
【Datawhale2609】算子开发实战 task01-熟悉沐曦GPU
人工智能·学习·大模型
青山木9 小时前
Hot 100 --- 最长递增子序列
java·数据结构·算法·leetcode·动态规划
传奇开心果编程9 小时前
【Xilem 0.4 基础语法学与练】第11课(深入篇):为什么其他 Rust 框架不是一切皆设计图
学习·rust·前端框架
爱吃苹果的日记本9 小时前
JAVA十三(练习课)
java·学习
土司大王9 小时前
LeetCode hot100——33.搜索旋转排序数组:Java 二分模板与 O(log n) 实现
数据结构·算法·leetcode
不穿鞋的懒羊羊9 小时前
STL容器
算法
huang5791479 小时前
多线程环境下的共享数据结构安全问题分析4
算法
平凡シンプル10 小时前
TCP UDP 网络通信插件 SL-Uniplugin-Tcp-Udp
网络·学习·uniapp·记录
zh路西法10 小时前
【玩转VLA具身智能机械臂】(四):点云分割与 OBB 夹取——从 PCA 失效到主动扫描
c++·机械臂·pca·vla·planner·obb·moveit2
RisunJan10 小时前
【地球脉动】地球日报 · 2026-09-14(过去24小时,非头条视角)
学习