• AcWing 362. 区间


    题目传送门

    本题同样考察差分约束难点在于想到用类似前缀和的方法去求解。

    题目中有两个地方可以想到需要使用前缀和,第一个地方是集合\(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) >= s(i - 1) \ \ ①$$
    即:

    s(i) >= s(i-1) + 0
    

    另外第\(i\)个元素要么在集合中要么不在,这意味着\(s(i)\)至多比\(s(i - 1)\)\(1\),所以有$$s(i-1) >= s(i) - 1 \ \ ②$$
    即:

    s(i-1)>= s(i) - 1
    

    最后需要加上题目中的约束条件,在\(a\)\(b\)区间中出现的个数不少于\(c\)个,即$$s(b) - s(a - 1) >= 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\)。总的代码如下:

    #include <bits/stdc++.h>
    using namespace std;
    //这里也可以把[a,b]区间往右移动一个单位,
    //这样就可以空出来S[0]这个点,作为超级原点
    const int N = 50010, M = 150010;
    
    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 t = q.front();
            q.pop();
            st[t] = false;
            for (int i = h[t]; ~i; i = ne[i]) {
                int j = e[i];
                if (dist[j] < dist[t] + w[i]) {
                    dist[j] = dist[t] + w[i];
                    if (!st[j]) {
                        q.push(j);
                        st[j] = true;
                    }
                }
            }
        }
    }
    
    int main() {
        cin >> m;
        memset(h, -1, sizeof h);
        //初始化差分约束的图
        for (int i = 1; i < N; i++)
            // s(n):集合从[1,n]中选择整数的个数
            // 这两个不等式关系是人想出来的吗?太BT了~
            // s(i)  >= s(i-1) + 0
            // s(i-1)>= s(i) - 1
            add(i - 1, i, 0), add(i, i - 1, -1);
    
        for (int i = 0; i < m; i++) {
            int a, b, c;
            cin >> a >> b >> c;
            add(a, b + 1, c); // s(b) >= s(a-1) + c
        }
        spfa();
        printf("%d\n", dist[50001]);
        return 0;
    }
    
  • 相关阅读:
    2020牛客暑期多校训练营(第五场)D 思维|最长上升子序列
    codeforces-1343E(贪心+BFS)
    2020牛客暑期多校训练营(第三场)C 计算几何
    codeforces-1385E(拓扑排序)
    2020牛客寒假算法基础训练营2
    2020牛客寒假算法基础训练营1
    codeforces-1295D(欧拉函数)
    codeforces-1283D(多源BFS)
    深入理解JVM之JVM内存区域与内存分配
    属性动画详解一(Property Animation)
  • 原文地址:https://www.cnblogs.com/littlehb/p/16060242.html
Copyright © 2020-2023  润新知