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