[AGC061C] First Come First Serve 题解

Description

nn 个人来过,第 ii 个人在 aia_i 时刻来在 bib_i 时刻走,每个人可以在来时或走时登记,问可能的登记顺序有多少种。

n5×105n\leq 5\times 10^5ai,bia_i,b_i 互不相同,i<n,ai<ai+1,bi<bi+1\forall i<n,a_i<a_{i+1},b_{i}<b_{i+1}

Solution

首先如果每个人随便选,有 2n2^n 种方案。但这显然会算重。考虑构造一种方案使得选的方案和最终排列一一对应。

按照排列从小到大的顺序,如果当前 (ai,bi)(a_i,b_i) 中有数,则选 aia_i,否则选 bib_i,容易发现这样做一定能够一一对应。

这时一个选择不合法当且仅当对于某个 ii(ai,bi)(a_i,b_i) 中没数但 ii 选了 bib_i,可以对不合法的位置数进行容斥。

具体的,如果选了 aia_ibib_i,则系数为 11(ai,bi)(a_i,b_i) 中没数且选 bib_i,则系数为 1-1

容易发现对于任意两个钦定了不合法的位置 i,ji,j[ai,bi][a_i,b_i][aj,bj][a_j,b_j] 一定不相交。并且如果设 preipre_i 表示最大的 bj<aib_j<a_ijjnxtinxt_i 表示最小的 aj>bia_j>b_ijj,那么如果 ii 不合法,(prei,i)(pre_i,i)(i,nxti)(i,nxt_i) 选择一定是固定的。这样就可以 dp 了。

fif_i 表示前 ii 个数的系数之和。那么 fi=2fi1nxtj+1=ifprejf_i=2f_{i-1}-\sum_{nxt_j+1=i}{f_{pre_j}}

时间复杂度:O(n)O(n)

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#include <bits/stdc++.h>

// #define int int64_t

const int kMaxN = 5e5 + 5, kMod = 998244353;

int n;
int a[kMaxN], b[kMaxN], pre[kMaxN], nxt[kMaxN], f[kMaxN];
std::vector<int> vec[kMaxN];

constexpr int qpow(int bs, int64_t idx = kMod - 2) {
int ret = 1;
for (; idx; idx >>= 1, bs = (int64_t)bs * bs % kMod)
if (idx & 1)
ret = (int64_t)ret * bs % kMod;
return ret;
}

inline int add(int x, int y) { return (x + y >= kMod ? x + y - kMod : x + y); }
inline int sub(int x, int y) { return (x >= y ? x - y : x - y + kMod); }
inline void inc(int &x, int y) { (x += y) >= kMod ? x -= kMod : x; }
inline void dec(int &x, int y) { (x -= y) < 0 ? x += kMod : x; }

void dickdreamer() {
std::cin >> n;
for (int i = 1; i <= n; ++i) std::cin >> a[i] >> b[i];
for (int i = 1, j = 0; i <= n; ++i) {
for (; b[j] < a[i]; ++j) {}
pre[i] = j;
}
a[n + 1] = b[n + 1] = 2 * n + 1;
for (int i = n, j = n + 1; i; --i) {
for (; a[j] > b[i]; --j) {}
nxt[i] = j;
}
for (int i = 1; i <= n; ++i) vec[nxt[i]].emplace_back(pre[i] - 1);
f[0] = 1;
for (int i = 1; i <= n; ++i) {
f[i] = 2ll * f[i - 1] % kMod;
for (auto j : vec[i]) dec(f[i], f[j]);
}
std::cout << f[n] << '\n';
}

int32_t main() {
#ifdef ORZXKR
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
int T = 1;
// std::cin >> T;
while (T--) dickdreamer();
// std::cerr << 1.0 * clock() / CLOCKS_PER_SEC << "s\n";
return 0;
}

[AGC061C] First Come First Serve 题解
https://sobaliuziao.github.io/2024/10/08/post/6eed6785.html
作者
Egg_laying_master
发布于
2024年10月8日
许可协议