HDU 6621 主席树 + 二分

啥,又是主席树?连续三场比赛都有主席树,然后都不会。。

题目链接

题意

给你一个序列, 告诉你L,R,P,K,让你求在L,R这段区间内,P与区间内数字的距离的第K小值

思路

求第K小,自然是主席树,但是这里并不单纯是求原序列的第K小,而是距离的第K小,然后我就母鸡了。其实有时候我们求第K小值还可以用二分,枚举上下界,第K小值满足的条件是有至少K个数字小于等于这个数。然后就二分就OK了。那么在这里,我们枚举距离值,那么我们就看看在这段区间内是有多少个数字是 在P - 距离 到 P + 距离的,如果大于K,就要缩小这个距离,我们要求的是那个最小的区间内数字个数等于K的那个数字。主席树在这里的应用就是给你一段区间,让你求这段区间内大于A小于B的数字的个数。这道题我以前用的是离散化,查询时按照地址查询,这里就用按值查询,很简便。

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
#include <set>
#include <map>
#include <stack>
#include <queue>
#include <ctime>
#include <cmath>
#include <cstdio>
#include <vector>
#include <bitset>
#include <string>
#include <cstring>
#include <sstream>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <functional>
#define eps 1e-8
#define PI acos(-1.0)

using namespace std;
const int maxn = 100005;
int a[maxn ], b[maxn],rt[maxn];
typedef long long ll;
struct node{
int l, r, sum;
}tree[maxn * 50];
int tot;

void build(int &o, int l, int r )
{
o = ++tot;
tree[o].sum = 0;
if(l == r) return ;
int mid = (l + r) >> 1;
build(tree[o].l , l, mid);
build(tree[o].r, mid + 1, r);
}

void update(int &o, int last, int l, int r, int pos)
{
o =++tot;
tree[o].l = tree[last].l;
tree[o].r = tree[last].r;
tree[o].sum = tree[last].sum + 1;
if(l == r) return;
int mid = (l + r) >> 1;
if(pos <= mid) update(tree[o].l, tree[last].l, l, mid, pos);
else update(tree[o].r , tree[last].r, mid + 1, r, pos);
}

int sum ;
void query(int o, int last, int l, int r, int left, int right)
{
if(left > b[r] || right < b[l]) return;
if(left <= b[l] && right >= b[r])
{
sum += tree[o].sum - tree[last].sum;
return;
}
int mid = (l + r) >> 1;
query(tree[o].l, tree[last].l, l, mid, left, right);
query(tree[o].r, tree[last].r, mid +1, r, left, right);

}
int main()
{
int T,n,Q,L,R,K,P;
scanf("%d",&T);
while(T--)
{
tot = 0;
scanf("%d %d",&n,&Q);
for(int i = 1; i <= n; ++i)
{
scanf("%d",&a[i]);
b[i] = a[i];
}
sort(b + 1, b + n + 1);
int N = unique(b + 1, b + n + 1) - b - 1;
build(rt[0],1 ,N);
for(int i = 1; i <= n; ++i)
{
a[i] = lower_bound(b + 1, b + N + 1, a[i]) - b;
update(rt[i], rt[i - 1], 1, N, a[i]);
}
int res = 0;
while(Q--)
{
scanf("%d %d %d %d",&L,&R,&P,&K);
L ^= res, R ^= res, K ^= res, P ^= res;
int l = 0, r = 1000005;
while(l <= r)
{
int mid = (l + r) / 2;
sum = 0;
query(rt[R], rt[L - 1], 1, N, P - mid, P + mid);
if(sum >= K){
r = mid - 1;
}
else
l = mid + 1;
}
cout << l << '\n';
res = l;
}
}
}