Loading

CF1965F Conference

记录一个自己切掉的 *3300。

首先注意到这是个匹配问题,根据形式很容易想到 hall 定理。乍一看认为对于一段区间的判定只需要判定所有子串就行了。

下面合法相当于是 hall 定理中的 \(|S|\le |N(S)|\),满足条件则相当于是存在完备匹配。

考虑这个怎么判,我先考虑了对于一个段 \([l,r]\),如果自己合法且 \([l,r-1]\)\([l+1,r]\) 都满足条件,那么 \([l,r]\) 也满足条件。那么我们维护一个 01 串,初始全是 \(1\),表示对于当前的 \(k\),以 \(i\) 为左端点的区间是满足条件,然后在 \(k+1\) 层,我们只需要判断段长为 \(k+1\) 时有哪些不合法段就行了。但是难以找到不合法段,不过换种思路,我们考虑对于每个 \(r\) 找到以 \(r\) 为右端点的最短不合法段,然后包含这个段的段都不能满足条件。

每个右端点 \(r\) 的最短不合法段怎么找呢?我们写下式子,一个段合法当且仅当 \(n-pre_{l-1}-suf_{r+1}\ge r-l+1\)。移一下项,变为 \(n-suf_{r+1}-r\ge pre_{l-1}-(l-1)\)

其中 \(pre_i\) 表示右端点小于等于 \(i\) 的讲课时间段,\(suf_i\) 表示左端点大于等于 \(i\) 的讲课时间段。

那么我们对任意 \(i\),使 \(pre_i\leftarrow pre_i-i\),那么式子变成了 \(n-suf_{r+1}-r\ge pre_{l-1}\)。我们用 rmq 维护 \(pre\),然后二分就行了。

交上去,发现第二十九个点 WA 了,以为写锅了,但经过检查发现并没有锅。

再进行一段时间的思考发现,不合法的集合并不一定是连续的一段!没事,冷静思考不要慌,分析造成这种情况的原因。令 \(l1\le r1+1<l2\le r2\),若 \([l1,r1]\)\([l2,r2]\)\([r1+1,l2-1]\)\([l1,r2]\) 都合法,而 \([l1,r1]\cup[l2,r2]\) 却不合法,发现原因是 \([r1+1,l2-1]\) 这个段内存在很多讲课时间段,这会导致误判。

显然我们存在很多讲课时间段都是多余的,我们考虑保留有用的段。我们发现在判定时如果我们考虑到了 \(i\),我们会用包含它的段中 \(r\) 最小的段去跟 \(i\) 匹配。那么我们总体跑一边贪心显然是对的,这样对于每个 \(i\) 都最多只有一个以它为左端点的讲课时间段了,那么就不会出现上面的情况。

然后就做完了,时间复杂度 \(\mathcal{O}(n\log n)\)

代码:

#include <bits/stdc++.h>
#define rep(i, l, r) for (int i (l); i <= (r); ++ i)
#define rrp(i, l, r) for (int i (r); i >= (l); -- i)
#define pii pair <int, int>
#define eb emplace_back
#define ls p << 1
#define rs ls | 1
using namespace std;
constexpr int N = 2e5 + 5, B = 71, P = 1042702009;
typedef unsigned long long ull;
inline int rd () {
  int x = 0, f = 1;
  char ch = getchar ();
  while (! isdigit (ch)) {
    if (ch == '-') f = -1;
    ch = getchar ();
  }
  while (isdigit (ch)) {
    x = (x << 1) + (x << 3) + ch - 48;
    ch = getchar ();
  }
  return x * f;
}
int qpow (int x, int y) {
  int ret = 1;
  for (; y; y >>= 1, x = x * x % P) if (y & 1) ret = ret * x % P;
  return ret;
}
int n, m, pre[N], suf[N];
vector <int> vec[N];
int diff[N];
int f[N][23];
int qry (int l, int r) {
  int k = __lg (r - l + 1);
  return max (f[l][k], f[r - (1 << k) + 1][k]);
}
int main () {
  // freopen ("1.in", "r", stdin);
  // freopen ("1.out", "w", stdout);
  n = 2e5; m = rd (); int cm = m;
  rep (i, 1, m) {
    int l = rd (), r = rd ();
    vec[l].eb (r);
    // ++ pre[r], ++ suf[l];
  }
  priority_queue <int> q;
  m = 0;
  rep (i, 1, n) {
    for (auto j : vec[i]) q.push (- j);
    vec[i].clear ();
    while (! q.empty () && - q.top () < i) q.pop ();
    if (! q.empty ()) {
      ++ pre[- q.top ()], q.pop (), ++ suf[i], ++ m;
    }
  }
  rep (i, 1, n) -- pre[i];
  rep (i, 0, n) pre[i] += pre[i - 1], f[i][0] = pre[i];
  rep (j, 1, 22) {
    rep (i, 0, n - (1 << j) + 1) f[i][j] = max (f[i][j - 1], f[i + (1 << (j - 1))][j - 1]);
  }
  rrp (i, 1, n) suf[i] += suf[i + 1];
  rep (i, 1, n) {
    int l = 1, r = i;
    while (l <= r) {
      int mid = l + r >> 1;
      if (qry (mid, i) <= m - suf[i + 1] - i) {
        r = mid - 1;
      } else l = mid + 1;
    }
    if (pre[r] > m - suf[i + 1] - i) vec[r + 1].eb (i);
  }
  int r = 2e5 + 1;
  rrp (i, 1, n) {
    for (auto u : vec[i]) r = min (r, u);
    ++ diff[1]; -- diff[r - i + 1];
  }
  rep (i, 1, m) {
    diff[i] += diff[i - 1];
    printf ("%d\n", diff[i]);
  }
  rep (i, m + 1, cm) puts ("0");
}
posted @ 2024-11-14 15:24  lalaouye  阅读(2)  评论(0编辑  收藏  举报