【洛谷】P8747 [蓝桥杯 2021 省 B] 双向排序 的题解
传送门
思路
这道题的意思就是有 n n n 个数,是从 1 1 1 到 n n n,和 m m m 次操作,每次操作输入一个 p p p 和 q q q, p p p 是 0 0 0 的时候把 1 1 1 到 q q q 降序排, p p p 是 1 1 1 的时候把 q q q 到 n n n 升序排。
根据数据范围你会发现, n n n 和 m m m 最大可以达到 1 0 5 10^5 105,如果每次操作我们都排序一下的话,时间复杂度是 O ( m n l o g n ) O(mnlogn) O(mnlogn),会达到 1 0 11 10^{11} 1011 以上,这样做肯定是不行的。
其实我们会发现,每次排序都会浪费掉很多时间来处理一些不必要的东西,或者说有很多操作是重复的。所以,我们先将 [ 1 , x ] [1,x] [1,x] 降序排,再将 [ 1 , y ] [1,y] [1,y] 降序排,其实我们可以直接把 [ 1 , x ] [1,x] [1,x] 降序排这个操作去掉,这是因为 [ 1 , x ] [1,x] [1,x] 只是 [ 1 , y ] [1,y] [1,y] 的一部分。从 [ x , n ] [x,n] [x,n] 升序排也是一样的道理,我们先将 [ x , n ] [x,n] [x,n] 升序排,再将 [ y , n ] [y,n] [y,n] 升序排,其实我们可以直接把 [ x , n ] [x,n] [x,n] 升序排这个操作去掉。
这样子我们可以发现每次降序排和升序排我们只取范围最大的那个,范围比它小或者等于它的操作其实都是不必要的,我们可以统统去掉。因此呢,我们最后真正需要的操作是升序排降序排依次交替的。我们可以发现,如果第一次操作是 [ x , n ] [x,n] [x,n] 升序排,那么我们也可以忽略掉,因为刚开始的时候我们的数据本来就是升序的,我们把部分升序排之后也是不会引起任何变化的。所以第一次操作必定是 [ 0 , x ] [0,x] [0,x] 降序排。
这样的话我们就可以找规律啦,例如:1 2 3 4 5 6 7 8 9
-
第一次我们将 [ 1 , 3 ] [1,3] [1,3] 降序排,变成:
3 2 1 4 5 6 7 8 9
-
第二次我们将 [ 7 , 9 ] [7,9] [7,9] 升序排,还是:
3 2 1 4 5 6 7 8 9
-
第三次我们将 [ 1 , 6 ] [1,6] [1,6] 降序排,变成:
6 5 4 3 2 1 7 8 9
-
第四次我们将 [ 4 , 9 ] [4,9] [4,9] 升序排,变成:
6 5 4 1 2 3 7 8 9
-
第五次我们将 [ 1 , 5 ] [1,5] [1,5] 降序排,变成:
6 5 4 2 1 3 7 8 9
相信你们发现了一个规律,就是排序的时候,我们会有一些数字被固定,这里我来证明一下。刚开始我们在 [ 1 , n ] [1,n] [1,n] 的时候,我们将 [ 1 , x ] [1,x] [1,x] 降序排列,由于最初我们的数据都是升序的,所以 ∀ b ∈ [ x + 1 , n ] > ∀ a ∈ [ 0 , x ] \forall b \in [x+1,n]> \forall a\in[0,x] ∀b∈[x+1,n]>∀a∈[0,x]。那么之后我们对 [ y , n ] [y,n] [y,n] 升序排列的话其实 [ x , n ] [x,n] [x,n] 这部分是不变的,注意看上面的 3 3 3 和 4 4 4,这是因为 [ y , x ] ∈ [ 0 , x ] < [ x + 1 , n ] [y,x]\in[0,x]<[x+1,n] [y,x]∈[0,x]<[x+1,n],所以 [ x + 1 , n ] [x+1,n] [x+1,n] 这部分的任意值是始终大于 [ y , x ] [y,x] [y,x] 中的任意值。因此我们对 [ y , n ] [y,n] [y,n] 升序排序的话,其实 [ x , n ] [x,n] [x,n] 这部分不会挪动位置。换句话说, [ x , n ] [x,n] [x,n] 已经被固定下来了。
我们观察一下上面的 1 1 1 和 2 2 2,它们没有任何交集,所以不会固定任何点,不过到了 3 3 3 和 4 4 4 的时候,他们就有交集了。并且发现, 1 1 1 和 2 2 2 这两次操作其实也是没有必要的,这是因为 [ 1 , 6 ] [1,6] [1,6] 中的 6 6 6 是大于 [ 1 , 3 ] [1,3] [1,3] 中的 3 3 3 的,还是上面的道理,这是因为 [ 1 , x ] [1,x] [1,x] 只是 [ 1 , y ] [1,y] [1,y] 的一部分。
那么还有一个地方,就是为啥接下来的
[
z
,
n
]
[z,n]
[z,n] 升序排也可以去掉? 我们可以这样想,后半段本身里面有一部分是升序排的,如果我们
z
z
z 在升序排那一部分后面,那这个操作本身就没有意义,比如:3 2 1 4 5 6 7 8 9
;
[
3
,
9
]
[3,9]
[3,9] 本身已经升序了,我们让
[
4
,
9
]
[4,9]
[4,9] 再升序排,也没有意义。那如果我们在3的前面升序排,比如
[
2
,
9
]
[2,9]
[2,9],就会变成 3 1 2 4 5 6 7 8 9
,之后我们对一个大于等于
3
3
3 的降序排,例如
[
1
,
6
]
[1,6]
[1,6] 就会变成 6 5 4 3 2 1 7 8 9
,你会发现我们直接对 3 1 2 4 5 6 7 8 9
的
[
1
,
6
]
[1,6]
[1,6] 降序排结果也是这样。
代码
#include <bits/stdc++.h>
#define endl "\n"
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
int n, m, ans[100005];
PII stk[100005];
int main() {
//freopen(".in","r",stdin);
//freopen(".out","w",stdout);
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> n >> m;
int top = 0;
while(m --) {
int p, q;
cin >> p >> q;
if (!p) {
while(top && stk[top].first == 0) q = max(q, stk[top --].second);
while(top >= 2 && stk[top - 1].second <= q) top -= 2;
stk[++ top] = {0, q};
}
else if (top) {
while (top && stk[top].first == 1) q = min(q, stk[top -- ].second);
while (top >= 2 && stk[top - 1].second >= q) top -= 2;
stk[++ top] = {1, q};
}
}
int k = n, l = 1, r = n;
for (int i = 1; i <= top; i ++) {
if (stk[i].first == 0)
while (r > stk[i].second && l <= r) ans[r --] = k --;
else
while (l < stk[i].second && l <= r) ans[l ++] = k --;
if (l > r) break;
}
if (top % 2)
while (l <= r) ans[l ++] = k --;
else
while (l <= r) ans[r --] = k --;
for (int i = 1; i <= n; i ++)
cout << ans[i] << " ";
return 0;
}