AcWing 362. 区间

AcWing 362. 区间

一、题目描述

给定 n 个区间 [ai,bi] 和 n 个整数 ci

你需要构造一个整数集合 Z,使得 i[1,n]Z 中满足 aixbi 的整数 x 不少于 ci 个。

求这样的整数集合 Z 最少 包含多少个数。

输入格式
第一行包含整数 n

接下来 n 行,每行包含三个整数 ai,bi,ci

输出格式
输出一个整数表示结果。

数据范围
1n50000,
0ai,bi50000,
0cibiai+1

输入样例

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

输出样例

6

二、题目解析

本题同样考察差分约束,难点在于想到用 前缀和 的方法去求解。题目中有两个地方可以想到需要使用前缀和:

  • 第一个地方是集合Z中在[ai,bi]中的的x不少于ci个,要求一个集合在若干个区间中数的个数,最快的方法就是使用前缀和

  • 另一个是只要求集合在给定区间中数的个数不小于c,但是这些数的选取并不是唯一的,所以Z中具体数的值并不重要,只需要知道在每个区间内Z中元素的个数是多少,这里也 暗示了使用前缀和

s[i]表示集合Z中有s[i]个元素是在1i之间的,则在区间[a,b]中就存在s[b]s[a1]个集合中的元素。

下面将题目条件 转化为差分约束的条件

  • ① 前缀和中默认的条件:s[i1]<=s[i]
  • ② 第i个元素要么在集合中、要么不在,这意味着s[i]至多比s[i1]1,所以有s[i1]>=s[i]1
  • ③ 需要加上题目中的约束条件,在ab区间中出现的个数不少于c个,即s[b]s[a1]>=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;
}
posted @   糖豆爸爸  阅读(90)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
历史上的今天:
2019-03-26 IDEA手动创建JFinal项目
2019-03-26 工作流选型
2019-03-26 记录sql server中数据创建时间和最后修改时间,方便查找问题
Live2D
点击右上角即可分享
微信分享提示