【题目描述】
在$2016$年,佳媛姐姐喜欢上了数字序列。因而她经常研究关于序列的一些奇奇怪怪的问题,现在她在研究一个难题,需要你来帮助她。

这个难题是这样子的:给出一个$1$到$n$的排列,现在对这个排列序列进行$m$次局部排序,排序分为两种:

  • 0 l r 表示将区间$[l,r]$的数字升序排序
  • 1 l r 表示将区间$[l,r]$的数字降序排序

注意,这里是对下标在区间$[l,r]$内的数排序。
最后询问第$q$位置上的数字。

【输入格式】
输入数据的第一行为两个整数$n$和$m$,$n$表示序列的长度,$m$表示局部排序的次数。

第二行为$n$个整数,表示$1$到$n$的一个排列。

接下来输入$m$行,每一行有三个整数$\text{op},l,r$,$\text{op}$为$0$代表升序排序,$\text{op}$为$1$代表降序排序,$l,r$表示排序的区间。

最后输入一个整数$q$,表示排序完之后询问的位置。

【输出格式】
输出数据仅有一行,一个整数,表示按照顺序将全部的部分排序结束后第$q$位置上的数字。

$n,m≤10_5$,$1\leq q\leq n$

题解

发现$m$非常大 用一般的排序肯定会超时

有什么东西处理区间操作比较快呢 当然是线段树啦

但是线段树并不支持区间排序

所以我们把问题转换一下:

如果要我们用线段树排序一个只由0和1组成的序列 显然是可以通过区间求和以及区间修改做到的

我们可以二分答案$mid$然后把输入排列中所有大于等于$mid$的置为1 其余的置为0

然后用线段树来进行这$m$次对于01序列的排序

排好后 如果位置$q$上的数字为1 则表示$mid$大于等于答案 否则则是$mid$小于答案

时间复杂度$O(n \log n + m \log^2 n)$

代码

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
#include <bits/stdc++.h>
#define lson ind<<1
#define rson ind<<1|1
using namespace std;

inline int read() {
int x = 0, f = 1; char ch = getchar();
for (; ch > '9' || ch < '0'; ch = getchar()) if (ch == '-') f = -1;
for (; ch <= '9' && ch >= '0'; ch = getchar()) x = (x << 1) + (x << 3) + (ch ^ '0');
return x * f;
}

int n, m, k, a[100005], tp[100005], le[100005], ri[100005], tmp[100005];

namespace Segtree{
struct segtree{
int l, r, sum, tag;
} tr[400005];

void build(int ind, int l, int r) {
tr[ind].l = l; tr[ind].r = r; tr[ind].tag = -1;
if (l == r) {
tr[ind].sum = tmp[l]; return;
}
int mid = (l + r) >> 1;
build(lson, l, mid); build(rson, mid+1, r);
tr[ind].sum = tr[lson].sum + tr[rson].sum;
}

inline void pushdown(int ind) {
if (tr[ind].tag == -1) return;
int v = tr[ind].tag; tr[ind].tag = -1;
tr[lson].tag = v; tr[lson].sum = (tr[lson].r - tr[lson].l + 1) * v;
tr[rson].tag = v; tr[rson].sum = (tr[rson].r - tr[rson].l + 1) * v;
}

void update(int ind, int x, int y, int v) {
if (x > y) return;
int l = tr[ind].l, r = tr[ind].r;
if (x <= l && r <= y) {
tr[ind].tag = v; tr[ind].sum = (r - l + 1) * v; return;
}
int mid = (l + r) >> 1;
pushdown(ind);
if (x <= mid) update(lson, x, y, v);
if (mid < y) update(rson, x, y, v);
tr[ind].sum = tr[lson].sum + tr[rson].sum;
}

int query(int ind, int x, int y) {
int l = tr[ind].l, r = tr[ind].r;
if (x <= l && r <= y) {
return tr[ind].sum;
}
int mid = (l + r) >> 1, ret = 0;;
pushdown(ind);
if (x <= mid) ret += query(lson, x, y);
if (mid < y) ret += query(rson, x, y);
return ret;
}
}

using namespace Segtree;

bool check(int x) {
for (int i = 1; i <= n; i++) tmp[i] = (a[i] >= x);
build(1, 1, n);
for (int i = 1; i <= m; i++) {
int num = query(1, le[i], ri[i]);
if (!tp[i]) {
update(1, le[i], ri[i]-num, 0); update(1, ri[i] - num + 1, ri[i], 1);
} else {
update(1, le[i], le[i] + num - 1, 1); update(1, le[i] + num, ri[i], 0);
}
}
return query(1, k, k) == 1;
}

int main() {
n = read(); m = read();
for (int i = 1; i <= n; i++) a[i] = read();
for (int i = 1; i <= m; i++) tp[i] = read(), le[i] = read(), ri[i] = read();
k = read();
int l = 1, r = n, mid, ans = 0;
while (l <= r) {
mid = (l + r) >> 1;
if (check(mid)) {
ans = mid; l = mid + 1;
} else r = mid - 1;
}
printf("%d\n", ans);
return 0;
}

评论