Files
python/TangDou/AcWing/ChaFenYueShu/362.md
HuangHai 1f397eca87 'commit'
2025-08-30 18:35:01 +08:00

117 lines
4.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

## [$AcWing$ $362$. 区间](https://www.acwing.com/problem/content/364/)
### 一、题目描述
给定 $n$ 个区间 [$a_i,b_i$] 和 $n$ 个整数 $c_i$。
你需要构造一个整数集合 $Z$,使得 $∀i∈[1,n]$,$Z$ 中满足 $a_i≤x≤b_i$ 的整数 $x$ 不少于 $c_i$ 个。
求这样的整数集合 $Z$ **最少** 包含多少个数。
**输入格式**
第一行包含整数 $n$。
接下来 $n$ 行,每行包含三个整数 $a_i,b_i,c_i$。
**输出格式**
输出一个整数表示结果。
**数据范围**
$1≤n≤50000$,
$0≤a_i,b_i≤50000$,
$0≤c_i≤b_i−a_i+1$
**输入样例**:
```cpp {.line-numbers}
5
3 7 3
8 10 3
6 8 1
1 3 1
10 11 1
```
**输出样例**:
```cpp {.line-numbers}
6
```
### 二、题目解析
本题同样考察差分约束,难点在于想到用 **前缀和** 的方法去求解。题目中有两个地方可以想到需要使用前缀和:
- 第一个地方是集合$Z$中在$[a_i,b_i]$中的的$x$不少于$c_i$个,**要求一个集合在若干个区间中数的个数,最快的方法就是使用前缀和**。
- 另一个是只要求集合在给定区间中数的个数不小于$c$,但是这些数的选取并不是唯一的,所以$Z$中具体数的值并不重要,只需要知道在每个区间内$Z$中元素的个数是多少,这里也 **暗示了使用前缀和**。
设$s[i]$表示集合$Z$中有$s[i]$个元素是在$1$到$i$之间的,则在区间$[a,b]$中就存在$s[b] - s[a - 1]$个集合中的元素。
下面将题目条件 **转化为差分约束的条件**,
- ① 前缀和中默认的条件:$s[i - 1] <= s[i]$
- ② 第$i$个元素要么在集合中、要么不在,这意味着$s[i]$至多比$s[i - 1]$多$1$,所以有$s[i-1] >= s[i] - 1$
- ③ 需要加上题目中的约束条件,在$a$到$b$区间中出现的个数不少于$c$个,即$s[b] - s[a - 1] >= c$
需要注意的是$a$和$b$的范围是从$0$开始的,为了在区间左端点等于$0$时使用前缀和数组不发生越界,需要将所有的区间均右移一个单位。最多有$50000$左右个点,上面的三类约束条件都可能出现$50000$来次,所以最多有$150000$左右条边。要想求集合中至少包含多少个数,只需要求$s[50001]$的最小值即可(这里向右偏移了$1$个单位,所以是$50001$),**求不等式组解的最小值自然是求最长路了**。建图时虚拟源点$0$到$1$,$1$到$2$,...都是有连边的,所以所有的点都是可达的。集合$Z$中最坏情况是包含了$50000$以内所有的点,所以一定是有解的。最后补充一句,这里的前缀和$s[i]$在$spfa$算法中也就是距离数组$d$。
#### $Code$
```cpp {.line-numbers}
#include <bits/stdc++.h>
using namespace std;
// 这里也可以把[a,b]区间往右移动一个单位,
// 这样就可以空出来S[0]这个点,作为超级原点
const int N = 50010, M = N * 3 + 10;
int dist[N];
int m; // m个区间,可以理解为m条边
bool st[N];
// 邻接表
int e[M], h[N], idx, w[M], ne[M];
void add(int a, int b, int c) {
e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++;
}
void spfa() {
queue<int> q;
// 求最大路径长度
memset(dist, -0x3f, sizeof dist);
dist[0] = 0;
st[0] = true;
q.push(0);
while (q.size()) {
int u = q.front();
q.pop();
st[u] = false;
for (int i = h[u]; ~i; i = ne[i]) {
int v = e[i];
if (dist[v] < dist[u] + w[i]) {
dist[v] = dist[u] + w[i];
if (!st[v]) {
q.push(v);
st[v] = true;
}
}
}
}
}
int main() {
cin >> m;
memset(h, -1, sizeof h);
/*
① s(i) >= s(i-1) + 0
② s(i-1)>= s(i) - 1
*/
for (int i = 1; i < N; i++) add(i - 1, i, 0), add(i, i - 1, -1);
while (m--) {
int a, b, c;
cin >> a >> b >> c;
a++, b++;
add(a - 1, b, c); // s(b) >= s(a-1) + c
}
spfa();
printf("%d\n", dist[50001]);
return 0;
}
```