Loading

CF1965F Conference

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

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

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

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

每个右端点 r 的最短不合法段怎么找呢?我们写下式子,一个段合法当且仅当 nprel1sufr+1rl+1。移一下项,变为 nsufr+1rprel1(l1)

其中 prei 表示右端点小于等于 i 的讲课时间段,sufi 表示左端点大于等于 i 的讲课时间段。

那么我们对任意 i,使 preipreii,那么式子变成了 nsufr+1rprel1。我们用 rmq 维护 pre,然后二分就行了。

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

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

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

然后就做完了,时间复杂度 O(nlogn)

代码:

#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");
}

作者:lalaouye

出处:https://www.cnblogs.com/lalaouyehome/p/18546051

版权:本作品采用「114514」许可协议进行许可。

posted @   lalaouye  阅读(4)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
more_horiz
keyboard_arrow_up dark_mode palette
选择主题
点击右上角即可分享
微信分享提示