题目链接:K小数查询

题意:给你一个长度为$n$序列$A$,有$m$个操作,操作分为两种:

  • 输入$x,y,c$,表示对$i\in[x,y] $,令$A_{i}=min(A_{i},c)$
  • 输入$x,y,k$,表示询问区间$[x,y]$中的第$k$小数

思路:数据范围不是很大,可以分块来做,记录每个块已经更新过的最小值$imin[]$,询问时二分答案,然后求出$[x,y]$区间中小于等于$mid$的数的个数$cnt$,通过判断$cnt$与$k$的大小来改变$l,r$即可

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
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <vector>
#include <cmath>

using namespace std;

const int N = 100010;
const int INF = 0x3f3f3f3f;

int block, belong[N], num, l[N], r[N], imin[N];
int n, m, a[N];
vector<int> v[N];

void build()
{
block = sqrt(n);
num = n / block;
if (n % block) num++;
for (int i = 1; i <= num; i++)
l[i] = (i - 1) * block + 1, r[i] = i * block;
r[num] = n;
for (int i = 1; i <= n; i++)
belong[i] = (i - 1) / block + 1;
for (int i = 1; i <= num; i++) {
imin[i] = INF;
for (int j = l[i]; j <= r[i]; j++)
v[i].push_back(a[j]);
sort(v[i].begin(), v[i].end());
}
}

void reset(int x)
{
v[x].clear();
for (int i = l[x]; i <= r[x]; i++) {
a[i] = min(a[i], imin[x]);
v[x].push_back(a[i]);
}
sort(v[x].begin(), v[x].end());
}

void update(int x, int y, int c)
{
int bl = belong[x], br = belong[y];
if (bl == br) {
for (int i = x; i <= y; i++)
a[i] = min(a[i], c);
reset(bl);
return;
}
for (int i = x; i <= r[bl]; i++)
a[i] = min(a[i], c);
reset(bl);
for (int i = l[br]; i <= y; i++)
a[i] = min(a[i], c);
reset(br);
for (int i = bl + 1; i < br; i++)
imin[i] = min(imin[i], c);
}

int query(int x, int y, int c)
{
int bl = belong[x], br = belong[y], cnt = 0;
if (bl == br) {
for (int i = x; i <= y; i++)
if (a[i] <= c || imin[bl] <= c) cnt++;
return cnt;
}
for (int i = x; i <= r[bl]; i++)
if (a[i] <= c || imin[bl] <= c) cnt++;
for (int i = l[br]; i <= y; i++)
if (a[i] <= c || imin[br] <= c) cnt++;
for (int i = bl + 1; i < br; i++)
if (imin[i] <= c) cnt = cnt + r[i] - l[i] + 1;
else cnt = cnt + upper_bound(v[i].begin(), v[i].end(), c) - v[i].begin();
return cnt;
}

int ask(int x, int y, int k)
{
int l = -1000000000, r = 1000000000;
while (l < r) {
int mid = (l + r) / 2;
if (query(x, y, mid) >= k) r = mid;
else l = mid + 1;
}
return l;
}

int main()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++)
scanf("%d", &a[i]);
build();
for (int i = 1; i <= m; i++) {
int kd, x, y, c;
scanf("%d%d%d%d", &kd, &x, &y, &c);
if (1 == kd) update(x, y, c);
else printf("%d\n", ask(x, y, c));
}
return 0;
}