P9331 [JOISC 2023] 护照 题解

Description

护照是旅行家进入他国时使用的证件。

在一个星球上有 NN 个国家,从 11NN 编号。每个国家都签发一种护照。当旅行家获得由国家i (1iN)i \ (1 \le i \le N) 签发的护照后,他能够进入国家 Li,Li+1,,RiL_i, L_{i + 1}, \dots, R_i这里保证旅行家能够进入其签证的签发国。形式上地说, LiiRiL_i \le i \le R_i 必然成立。

你有一个爱旅行的朋友。即使他奢望能环游世界,但他最初一种护照也没有。因此,他想通过一下重复以下两项行为来环游这 NN 个国家。

  • 获得他当前所在国家签发的护照。
  • 用他现有的护照进入某个国家。

知道他的计划后,你想知道这个计划的是否可行,和如果可行的话,他最少需要的护照数量。因为你并不清楚他现在身处何国,所以你预测了 QQ 个他可能正居住在那的国家 X1,X2,,XQX_1, X_2, \dots, X_Q

现在给定各国护照的信息 Li,RiL_i, R_i 和他可能居住的 QQ 个国家,您需要写一个程序,对于每一个可能居住的国家,判断他是否可能环游这 NN 个国家,如果可能的话,计算出他需要的最少护照种数。

2N2×1052 \le N \le 2 \times 10 ^ 51LiiRiN1 \le L_i \le i \le R_i \le N

Solution

首先能到的国家一定是一段区间,如果设当前区间为 [L,R][L,R],则每次相当于是选择一个 i[L,R]i\in [L,R],让 Lmin{L,li},Rmax{R,ri}L\leftarrow\min\{L,l_i\},R\leftarrow\max\{R,r_i\}

然后有个观察是在当前的 [L,R][L,R] 里最多选择两个区间进行拓展,并且拓展后就再也不会选择 [L,R][L,R] 内的区间了,否则提前选一定更优。

fif_i 表示区间为 [li,ri][l_i,r_i] 的最小答案。

如果当前只选一个区间,则 fifj+1 (j[li,ri])f_i\leftarrow f_j+1\ (j\in[l_i,r_i])

如果选择两个区间 [lx,rx],[ly,ry][l_x,r_x],[l_y,r_y],则一定满足 lxli,ryril_x\leq l_i,r_y\geq r_i,此时由于 [li,ri][l_i,r_i] 不会再选了,那么 xxyy 一定是一个拓展左端点到 11,另一个是拓展右端点到 nn,否则一个在拓展的过程中一定会拓展到另一个。

这样的话如果求出 dis1idis1_i 表示将 [li,ri][l_i,r_i] 左端点拓展到 11 的答案,dis2idis2_i 表示将右端点拓展到 nn 的答案,就可以让 fidis1x+dis2y+1f_i\leftarrow dis1_x+dis2_y+1 即可。

考虑怎么求出 dis1idis1_idis2idis2_i。下面只考虑求 dis1idis1_i

由于此时只需要单方向拓展,所以只要让 dis1idis1j+1 (j[li,ri])dis1_i\leftarrow dis1_j+1\ (j\in [l_i,r_i]) 即可。

两部分都可以用线段树优化建图和 Dijkstra 求。

时间复杂度:O(nlog2n)O(n\log^2 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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
#include <bits/stdc++.h>

// #define int int64_t

const int kMaxN = 2e5 + 5;

int n, q;
int l[kMaxN], r[kMaxN], f[kMaxN], dis1[kMaxN], dis2[kMaxN], idx[kMaxN];
std::vector<std::pair<int, int>> G[kMaxN * 4];

struct SGT {
int N;
std::pair<int, int> mx[kMaxN * 4];
void pushup(int x) { mx[x] = std::max(mx[x << 1], mx[x << 1 | 1]); }
void build(int n, int *arr, int op = 1) {
for (N = 1; N <= n + 1; N <<= 1) {}
for (int i = 1; i <= n; ++i) mx[i + N] = {op * arr[i], i};
for (int i = N - 1; i; --i) pushup(i);
}
std::pair<int, int> query(int l, int r) {
std::pair<int, int> ret = {-1e9, 0};
for (l += N - 1, r += N + 1; l ^ r ^ 1; l >>= 1, r >>= 1) {
if (~l & 1) ret = std::max(ret, mx[l ^ 1]);
if (r & 1) ret = std::max(ret, mx[r ^ 1]);
}
return ret;
}
} sgtl, sgtr;

void build(int x, int l, int r) {
if (l == r) return void(idx[l] = x);
int mid = (l + r) >> 1;
build(x << 1, l, mid), build(x << 1 | 1, mid + 1, r);
G[x << 1].emplace_back(x, 0), G[x << 1 | 1].emplace_back(x, 0);
}

void update(int x, int l, int r, int ql, int qr, int p) {
if (l > qr || r < ql) return;
else if (l >= ql && r <= qr) return void(G[x].emplace_back(idx[p], 1));
int mid = (l + r) >> 1;
update(x << 1, l, mid, ql, qr, p), update(x << 1 | 1, mid + 1, r, ql, qr, p);
}

void dijkstra1(int s, int *res) {
static int dis[kMaxN * 4];
static bool vis[kMaxN * 4];
std::fill_n(dis + 1, 4 * n, 1e9);
std::fill_n(vis + 1, 4 * n, 0);
std::priority_queue<std::pair<int, int>> q;
for (int i = 1; i <= n; ++i)
if (l[i] <= s && s <= r[i])
dis[idx[i]] = 0, q.emplace(0, idx[i]);
for (; !q.empty();) {
int u = q.top().second; q.pop();
if (vis[u]) continue;
vis[u] = 1;
for (auto [v, w] : G[u]) {
if (dis[v] > dis[u] + w) {
dis[v] = dis[u] + w, q.emplace(-dis[v], v);
}
}
}
for (int i = 1; i <= n; ++i) res[i] = dis[idx[i]];
}

void dijkstra2() {
static int dis[kMaxN * 4];
static bool vis[kMaxN * 4];
std::fill_n(dis + 1, 4 * n, 1e9);
std::fill_n(vis + 1, 4 * n, 0);
std::priority_queue<std::pair<int, int>> q;
for (int i = 1; i <= n; ++i) {
dis[idx[i]] = f[i];
q.emplace(-dis[idx[i]], idx[i]);
}
for (; !q.empty();) {
int u = q.top().second; q.pop();
if (vis[u]) continue;
vis[u] = 1;
for (auto [v, w] : G[u]) {
if (dis[v] > dis[u] + w) {
dis[v] = dis[u] + w, q.emplace(-dis[v], v);
}
}
}
for (int i = 1; i <= n; ++i) f[i] = dis[idx[i]];
}

void prework() {
build(1, 1, n);
for (int i = 1; i <= n; ++i) update(1, 1, n, l[i], r[i], i);
dijkstra1(1, dis1), dijkstra1(n, dis2);
}

void getf() {
sgtl.build(n, dis1, -1), sgtr.build(n, dis2, -1);
for (int i = 1; i <= n; ++i) {
if (l[i] > 1) f[i] += dis1[sgtl.query(l[i], r[i]).second] + 1;
if (r[i] < n) f[i] += dis2[sgtr.query(l[i], r[i]).second] + 1;
// std::cerr << f[i] << " \n"[i == n];
}
dijkstra2();
}

void dickdreamer() {
std::cin >> n;
for (int i = 1; i <= n; ++i)
std::cin >> l[i] >> r[i];
prework(), getf();
std::cin >> q;
for (int i = 1; i <= q; ++i) {
int x;
std::cin >> x;
std::cout << (f[x] > n ? -1 : f[x] + 1) << '\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;
}

P9331 [JOISC 2023] 护照 题解
https://sobaliuziao.github.io/2025/02/10/post/aa034263.html
作者
Egg_laying_master
发布于
2025年2月10日
许可协议