• Luogu 3626 [APIO2009]会议中心


    很优美的解法。

    推荐大佬博客

    如果没有保证字典序最小这一个要求,这题就是一个水题了,但是要保证字典序最小,然后我就不会了……

    如果一条线段能放入一个区间$[l', r']$并且不影响最优答案,那么对于这条线段$[l, r]$,设$solve(l, r)$表示$[l, r]$这个区间里面最多能放多少条线段,一定要有条件$solve(l', l - 1) + solve(r + 1, r') + 1 == solve(l', r')$。

    那么贪心的时候顺便考虑一下怎么检验的问题就可以了,如果暴力检验是$O(n)$的,考虑优化,利用一下倍增思想,我们设$f_{i, j}$表示从$i$开始选择$2^{j}$条线段所能到达的最靠左的右端点,那么在离散化之后就可以用$O(nlogn)$的时间预处理出$f$数组,这样子每一次检验求$solve$的时间是$O(logn)$的,具体怎么处理可以参照下面的代码。

    考虑一下最后怎么进行的贪心,对于每一条线段,我们检验一下是不是能放,这个检验的过程可以用很多数据结构做到$O(logn)$,如果能放入,我们就找出之前放过的最前的右端点和最后的左端点,然后chk一下上面的条件是否成立就可以了。

    感觉这个过程写个线段树更直观一点,还是学习了一下上面那位大佬高能的树状数组写法。

    时间复杂度$O(nlogn)$。

    Code:

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    
    const int N = 2e5 + 5;
    const int Lg = 20;
    const int inf = 1 << 30;
    
    int n, tot = 0, maxn = 0;
    int l[N], r[N], f[N << 1][Lg];
    int ans[N], lmax[N << 1], rmax[N << 1], lsum[N << 1], rsum[N << 1];
    
    struct Innum {
        int val, id;
    } in[N << 1];
    
    bool cmpIn(const Innum &x, const Innum &y) {
        if(x.val != y.val) return x.val < y.val;
        else return x.id < y.id;
    }
    
    inline void read(int &X) {
        X = 0; char ch = 0; int op = 1;
        for(; ch > '9'|| ch < '0'; ch = getchar())
            if(ch == '-') op = -1;
        for(; ch >= '0' && ch <= '9'; ch = getchar())
            X = (X << 3) + (X << 1) + ch - 48;
        X *= op;
    }
    
    inline int max(int x, int y) {
        return x > y ? x : y;
    }
    
    inline int min(int x, int y) {
        return x > y ? y : x;
    }
    
    inline void chkMax(int &x, int y) {
        if(y > x) x = y;
    }
    
    inline void discrete() {
        sort(in + 1, in + 1 + tot, cmpIn);
        for(int cnt = 0, i = 1; i <= tot; i++) {
            if(in[i].val != in[i - 1].val) ++cnt;
            maxn = max(maxn, cnt);
            if(in[i].id > n) r[in[i].id - n] = cnt;
            else l[in[i].id] = cnt;
        }
    }
    
    #define lowbit(x) ((x) & (-x))
    
    inline void aSum(int *now, int x) {
        for(; x <= maxn; x += lowbit(x))
            ++now[x];
    }
    
    inline int qSum(int *now, int x) {
        int res = 0;
        for(; x > 0; x -= lowbit(x))
            res += now[x];
        return res;
    }
    
    inline void aMax(int *now, int x) {
        for(int t = x; x <= maxn; x += lowbit(x))
            chkMax(now[x], t);
    }
    
    inline int qMax(int *now, int x) {
        int res = 0;
        for(; x > 0; x -= lowbit(x))
            chkMax(res, now[x]);
        return res;
    }
    
    inline int solve(int ln, int rn) {
        int res = 0;
        for(int i = 18; i >= 0; i--)
            if(f[ln][i] <= rn) res += (1 << i), ln = f[ln][i] + 1;
        return res;
    }
    
    int main() {
        read(n);
        for(int i = 1; i <= n; i++) {
            read(l[i]), read(r[i]);
            in[++tot] = (Innum) {l[i], i};
            in[++tot] = (Innum) {r[i], i + n};
        }
        discrete();
    
        memset(f, 0x7f, sizeof(f));
        for(int i = 1; i <= n; i++)
            f[l[i]][0] = min(f[l[i]][0], r[i]);
        for(int i = maxn; i >= 1; i--) {
            f[i][0] = min(f[i][0], f[i + 1][0]);
            for(int j = 1; f[i][j - 1] <= maxn && (1 << j) <= n; j++)
                f[i][j] = f[f[i][j - 1] + 1][j - 1];
        } 
    
    /*  for(int i = 1; i <= maxn; i++)
            printf("%d ", f[i][0]);
        printf("
    ");   */
    
    /*  for(int i = 1; i <= n; i++)
            printf("%d %d
    ", l[i], r[i]);
        printf("
    ");   */
    
        tot = 0;
        for(int i = 1; i <= n; i++) {
            int v = tot - qSum(rsum, l[i] - 1) - qSum(lsum, maxn - r[i]);
            if(v > 0) continue;
            int lst = qMax(rmax, l[i] - 1), nxt = maxn - qMax(lmax, maxn - r[i]);
            if(solve(lst + 1, l[i] - 1) + solve(r[i] + 1, nxt) + 1 == solve(lst + 1, nxt)) {
                ans[++tot] = i;
                aSum(rsum, r[i]), aSum(lsum, maxn - l[i] + 1);
                aMax(rmax, r[i]), aMax(lmax, maxn - l[i] + 1);
            }
    
    //        printf("%d ", v);
        }
    //    printf("
    ");
    
        printf("%d
    ", tot);
        for(int i = 1; i <= tot; i++)
            printf("%d ", ans[i]);
        printf("
    ");
    
        return 0;   
    }
    View Code
  • 相关阅读:
    如何使用C++构建一个极坐标系?
    归一化 [a] 到 [b] 区间
    ffmpeg 如何转换 rgb 图片到 yuv420p?如何使用 C 语言实现 rgb24 如何转换 yuv420p ?
    如何写一个通用的网络包?
    jenkins 配置参数为tag
    jmeter函数助手digest使用简介
    RD-T: 3540 Front Impact Bumper Model
    Listary软件的使用
    Adams各种材料的接触力参数
    Spring 使用构造方法注入方式
  • 原文地址:https://www.cnblogs.com/CzxingcHen/p/9553116.html
Copyright © 2020-2023  润新知