You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

4.1 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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_ia_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]个元素是在1i之间的,则在区间[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
  • ③ 需要加上题目中的约束条件,在ab区间中出现的个数不少于c个,即s[b] - s[a - 1] >= c

需要注意的是ab的范围是从0开始的,为了在区间左端点等于0时使用前缀和数组不发生越界,需要将所有的区间均右移一个单位。最多有50000左右个点,上面的三类约束条件都可能出现50000来次,所以最多有150000左右条边。要想求集合中至少包含多少个数,只需要求s[50001]的最小值即可(这里向右偏移了1个单位,所以是50001求不等式组解的最小值自然是求最长路了。建图时虚拟源点01,12...都是有连边的,所以所有的点都是可达的。集合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;
}