题目大意
「Codeforces 1187D」Subarray Sorting
给定长度为 $n$ 的两个数列 $A, B$,问是否可以做若干次「将 $A$ 的某个区间升序排序」把 $A$ 变为 $B$。
数据范围:$n \le 3 \times 10^5$。
思路分析
发现自己最近连水题都不会做了 QAQ。
首先把「将 $A$ 的某个区间升序排序」这种操作替换成「将 $A$ 的某两个相邻的数升序排序」这种操作以方便思考。转化后的题目其实本质没有变化,因为我们可以用冒泡排序的方式实现「将一个区间排序」。
然后逐位考虑,假设现在考虑到 $A_i$,前 $i - 1$ 位已经排好了。那么我们如果要让操作过后的 $A_i$ 要和 $B_i$ 相等,就必须从 $A$ 的后面把一个等于 $B_i$ 的数调动到第 $i$ 位。我们找到数列 $A$ 从 $i$ 开始的第一个 $A_j = B_i$,考虑调动 $A_j$。不难发现这个数能被成功调动当且仅当所有 $A_k (i \le k < j)$ 都小于它自己。于是我们暴力地模拟这个过程即可得到一个 $O(n^2)$ 的算法。
可惜这并不能通过本题,考虑使用数据结构优化。我们可以直接使用平衡树维护这个过程,但我们还有更简单的方法。发现我们只需要用到一个数列没被调动过的数的前缀最大值。于是我们每次调动完一个数后,我们将它赋值为 $-1$,这样,它就不会被算进最大值中了。对于还可使用的数,我们使用线段树维护区间最大值即可。如何实现寻找 $A$ 中可使用的第一个 $A_j = B_i$ 呢?我们开 $n$ 个队列,第 $i$ 个存储 $A$ 中数值为 $i$ 的数的出现位置;每次找数时,我们只需取出队首即可。总时间复杂度 $O(n \log n)$。
代码实现
1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 | using namespace std; |
8 | |
9 | const int maxn = 3e5, maxm = maxn * 4; |
10 | int T, n, a[maxn + 3], b[maxn + 3], mx[maxm + 3]; |
11 | queue<int> app[maxn + 3]; |
12 | |
13 | void maintain(int x) { |
14 | mx[x] = max(mx[ls], mx[rs]); |
15 | } |
16 | |
17 | void modify(int x, int l, int r, int y, int z) { |
18 | if (l == r) { |
19 | mx[x] = z; |
20 | return; |
21 | } |
22 | if (y <= mid) { |
23 | modify(ls, l, mid, y, z); |
24 | } else { |
25 | modify(rs, mid + 1, r, y, z); |
26 | } |
27 | maintain(x); |
28 | } |
29 | |
30 | int query(int x, int l, int r, int lx, int rx) { |
31 | if (lx > rx) { |
32 | return 0; |
33 | } |
34 | if (l >= lx && r <= rx) { |
35 | return mx[x]; |
36 | } |
37 | int res = 0; |
38 | if (lx <= mid) { |
39 | res = max(res, query(ls, l, mid, lx, rx)); |
40 | } |
41 | if (rx > mid) { |
42 | res = max(res, query(rs, mid + 1, r, lx, rx)); |
43 | } |
44 | return res; |
45 | } |
46 | |
47 | int main() { |
48 | scanf("%d", &T); |
49 | while (T--) { |
50 | for (int i = 1; i <= n; i++) { |
51 | queue<int> t; |
52 | swap(t, app[i]); |
53 | } |
54 | for (int i = 1; i <= n * 4; i++) { |
55 | mx[i] = 0; |
56 | } |
57 | scanf("%d", &n); |
58 | for (int i = 1; i <= n; i++) { |
59 | scanf("%d", &a[i]); |
60 | a[i] = n - a[i] + 1; |
61 | app[a[i]].push(i); |
62 | modify(1, 1, n, i, a[i]); |
63 | } |
64 | for (int i = 1; i <= n; i++) { |
65 | scanf("%d", &b[i]); |
66 | b[i] = n - b[i] + 1; |
67 | } |
68 | bool flag = true; |
69 | for (int i = 1; i <= n; i++) { |
70 | if (app[b[i]].empty()) { |
71 | flag = false; |
72 | break; |
73 | } |
74 | int x = app[b[i]].front(); |
75 | app[b[i]].pop(); |
76 | if (query(1, 1, n, 1, x - 1) > a[x]) { |
77 | flag = false; |
78 | break; |
79 | } |
80 | modify(1, 1, n, x, 0); |
81 | } |
82 | puts(flag ? "YES" : "NO"); |
83 | } |
84 | return 0; |
85 | } |