4.1 KiB
AcWing
362
. 区间
一、题目描述
给定 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
输入样例:
5
3 7 3
8 10 3
6 8 1
1 3 1
10 11 1
输出样例:
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
#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;
}