AcWing 362. 区间

2021-01-27 21:12

阅读:694

标签:最大   add   位置   复杂   ==   space   int   研究   lse   

听书上说有贪心 + 数据结构的做法,研究了一下。

朴素贪心

考虑把所有线段按照右端点 \(b\) 从小到大排序,依次考虑每一条线段的要求:

  • 如果已经满足要求则跳过
  • 否则尽量选择靠后的数(因为之后的线段的右端点都在这条线段的右边,这样容错更高)

所以,我们可以建一个数组,\(d[i]\) 表示 \(i\) 数字是否选择(填\(1\)\(0\)),扫一遍 \([l, r]\) 区间求和,然后从后往前贪心放数即可。

对于每条线段需要 \(O(r - l + 1)\)。所以最坏情况下 \(O(n ^ 2)\)。但是轻松 \(52ms\) 过了。

#include 
#include 
#include 
using namespace std;
const int N = 50005;
int n, d[N], c[N];
struct Seg{
    int a, b, c;
    bool operator  0) {
            for (int j = r; j >= l && cnt; j--)
                if(!d[j]) cnt--, ans++, d[j] = 1;
        }
    }
    printf("%d\n", ans);
    return 0;
}

优化

考虑用数据结构优化。

发现我们需要三个操作:

  • 询问 \([l, r]\) 区间的数字个数
  • 将值为 \(x\) 的位置 \(+1\)
  • 从后往前,找到比当前位置靠前的下一个 \(0\) 的位置。
  1. 前两个就是 “区间求和,单调修改”,典型的树状数组。$O(nlog_250000) $

  2. 第三种操作,可以用并查集优化。为什么可以确保时间复杂度呢?对于每一条线段,最多只有一次会枚举到 \(1\) (即开始的那一次),之后每次枚举都会枚举到 \(0\) 的位置,即\(d[i] = 0\),然后把它变成 \(1\),而以后就不会访问到了。而一共有 \(50000\) 个值,所以复杂度是 \(O(50000log_n)\)

\(33ms\)

#include 
#include 
#include 
using namespace std;
const int N = 50001;
int n, d[N], c[N], f[N];
struct Seg{
    int a, b, c;
    bool operator  0) {
            for (int j = r; j >= l && cnt; ) {
                // d[j] == 1 的情况每条线段至多出现一次
                if(!d[j]) {
                    cnt--, ans++, d[j] = 1;
                    // j 被标记成 1 了,要指向 find(j - 1)
                    f[j] = j - 1;
                    // 维护树状数组
                    add(j);
                };
                if(find(j) != j) j = f[j];
                else j--;
            }
        }
    }
    printf("%d\n", ans);
    return 0;
}

AcWing 362. 区间

标签:最大   add   位置   复杂   ==   space   int   研究   lse   

原文地址:https://www.cnblogs.com/dmoransky/p/11923718.html


评论


亲,登录后才可以留言!