## [$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 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 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; } ```