差分约束

题目描述

在一条长度为 \(L = 5 \times 10^4\) 的线性轨道上,有 \(n\) 次经过一个物体。每个监测装置视为一个点,当轨道上有物体经过时会被触发。现在需要在轨道上部署若干检测装置(每个装置占据一个整数点位置),要求对于每个监测区域 \(i\),当有物体从 \(a_i\) 移动到 \(b_i\) 时,沿途至少触发 \(c_i\) 个检测装置(\(a_i\)\(b_i\) 也算沿途)。问最少需要部署多少个检测装置才能满足所有监测区域的触发需求?

输入格式

第一行一个整数 \(n\),表示物体个数;

以下 \(n\) 行每行描述这些物体,第 \(i+1\) 行三个整数 \(a_i,b_i,c_i\),由空格隔开。

输出格式

一行,输出满足要求的最少检测装置个数。

样例数据

输入数据 1

5
3 7 3
8 10 3
6 8 1
1 3 1
10 11 1

数据范围

对于 \(100\%\) 的数据,对于全部数据,\(1\le n\le 5\times 10^4,0\le a_i\le b_i\le 5\times 10^4,1\le c_i\le b_i-a_i+1\)

// 差分约束 + SPFA 解法思路:
// 1. 定义前缀变量 S[i] 表示在轨道点 1..i 上放置的检测装置总数,目标求 S[L]
// 2. 对于每个监测区间 [a,b] 需至少 c 个装置:
//      S[b] - S[a-1] >= c  ⇒  边 (u=a-1) -> (v=b), 权重 w=c
// 3. 每个点至多放一个装置:0 <= S[i] - S[i-1] <= 1,拆为两条约束:
//      S[i] - S[i-1] >= 0  ⇒  边 (i-1) -> i, w=0
//      S[i-1] - S[i] >= -1 ⇒  边 i -> (i-1), w=-1
// 4. 将所有约束建成有向图,对 S[i] 做“最长路”松弛:
//      初始化 dist[] = -INF, dist[0] = 0
//      对边 u->v,w 做 if(dist[v] < dist[u] + w) dist[v] = dist[u] + w
// 5. SPFA 实现最长路松弛,最终 dist[L] 即为所求最小满足解

#include <bits/stdc++.h>
using namespace std;
using ll = long long;

// 轨道最大长度 L,节点编号从 S_0 到 S_L 共 L+1 个
const int L = 50000;
const ll NEG_INF = -(1LL<<60);  // 用于初始化最小值

// 边结构:从 u -> v ,权重 w 表示 S[v] >= S[u] + w
struct Edge {
    int to;
    ll w;
};

// 邻接表存图:节点 0..L
vector<Edge> adj[L+1];

// 添加一条差分约束边:S[v] >= S[u] + w
void add_edge(int u, int v, ll w) {
    adj[u].push_back({v, w});
}

// SPFA 求解差分约束中的“最长路”问题
ll spfa() {
    vector<ll> dist(L+1, NEG_INF);
    vector<bool> inq(L+1, false);
    deque<int> q;
    
    // 初始化:令 S[0] = 0,其他 S[i] 从 -INF 开始
    dist[0] = 0;
    q.push_back(0);
    inq[0] = true;
    
    while (!q.empty()) {
        int u = q.front();
        q.pop_front();
        inq[u] = false;
        
        for (auto &e : adj[u]) {
            int v = e.to;
            ll w = e.w;
            if (dist[v] < dist[u] + w) {
                dist[v] = dist[u] + w;
                if (!inq[v]) {
                    inq[v] = true;
                    q.push_back(v);
                }
            }
        }
    }
    // 返回 S[L]
    return dist[L];
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int n;
    cin >> n;
    // 读入 n 条区间约束 [a, b, c]
    for (int i = 0; i < n; i++) {
        int a, b, c;
        cin >> a >> b >> c;
        int u = max(0, a - 1);
        int v = b;
        add_edge(u, v, c);
    }
    // 相邻点差分约束
    for (int i = 1; i <= L; i++) {
        add_edge(i-1, i, 0);
        add_edge(i, i-1, -1);
    }

    ll ans = spfa();
    cout << ans << "\n";
    return 0;
}

练习题:
https://www.luogu.com.cn/problem/P1645

学习参考资料:
https://oi-wiki.org/graph/diff-constraints/
https://www.cnblogs.com/TanJI-C/p/15162547.html

posted @ 2025-07-16 15:07  katago  阅读(19)  评论(0)    收藏  举报