牛客多校第七场 Find the median 权值线段树 + 区间离散化

相比较普通的线段树,权值线段树存的是每个区间的出现的次数,查找的时候也是根据这个权值来查找。

题目链接

题意

开始是一个空的序列,每次一个询问L,R,代表把L到R这R-L+1个数字加入到序列当中,每次放完,输出当前序列的中位数

思路

在这个题中,叶子节点存的也是一段区间,这段区间的个数不和普通的线段树一样,它的个数是人为确定的。我们把所有L,R离散化到一个数组中,还有注意的是R要先+1在存到数组中,这样存的就是一个左闭右开的区间了。然后初始建树的时候每个叶子节点存的区间就包括了所有的L,R区间。然后更新的时候,我们更新的是这个区间出现的次数,比如说第一次[1,3],第二次[1,4], 那么存[1,3]这段区间的那个线段树节点的权值就是2,因为它出现两次。

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
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
typedef long long ll;
const int maxn = 800005;
ll X[maxn],Y[maxn],L[maxn],R[maxn],b[maxn * 2];
ll A[10],B[10],C[10],M[10];
struct Node{
ll lazy, len, sum;
}tr[maxn << 2];

void pushdown(ll node)
{
if(tr[node].lazy == 0) return;
tr[node << 1].lazy += tr[node].lazy;
tr[node << 1|1].lazy += tr[node].lazy;
tr[node << 1].sum += tr[node].lazy * tr[node << 1].len;
tr[node << 1|1].sum += tr[node].lazy * tr[node << 1|1].len;
tr[node].lazy = 0;
}
void build(ll node, ll l, ll r)
{
tr[node].lazy = 0;
tr[node].sum = 0;
if(l == r)
{
tr[node].len = b[l + 1] - b[l];
return;
}
int mid = (l + r ) >> 1;
build(node << 1, l , mid);
build(node << 1|1, mid + 1, r);
tr[node].len = tr[node << 1].len + tr[node << 1|1].len;
}

void update(ll node, ll l, ll r, ll ul, ll ur)
{
if(ur < l || ul > r) return;
if(ul <= l && ur >= r)
{
tr[node].sum += tr[node].len;
tr[node].lazy ++;
return;
}
pushdown(node);
int mid = (l + r) >> 1;
update(node << 1, l , mid, ul ,ur);
update(node << 1|1, mid + 1, r, ul, ur);
tr[node].sum = tr[node << 1].sum + tr[node << 1|1].sum;
}


ll query(int node, ll l, ll r, ll k)
{
if(l == r)
{
ll cmt = tr[node].sum / tr[node].len;
return b[l] + (k - 1)/ cmt;
}
pushdown(node);
int mid = (l + r) >> 1;
if(k <= tr[node << 1].sum) return query(node << 1 , l, mid, k);
else return query(node << 1|1, mid + 1, r, k - tr[node << 1].sum);
}

int main()
{
ll n;
scanf("%lld",&n);
scanf("%lld%lld%lld%lld%lld%lld",&X[1],&X[2],&A[1],&B[1],&C[1],&M[1]);
scanf("%lld%lld%lld%lld%lld%lld",&Y[1],&Y[2],&A[2],&B[2],&C[2],&M[2]);
for(int i = 3; i <= n; ++i)
{
X[i] = (A[1] * X[i - 1] + B[1] * X[i - 2] + C[1]) % M[1];
Y[i] = (A[2] * Y[i - 1] + B[2] * Y[i - 2] + C[2]) % M[2];
}
int cnt = 0;
for(int i = 1; i <= n; ++i)
{
L[i] = min(X[i], Y[i]) + 1;
R[i] = max(X[i], Y[i]) + 1;
b[++cnt] = L[i];
b[++cnt] = R[i] + 1;
}
sort(b + 1, b + cnt + 1);
cnt = unique(b + 1, b + cnt + 1) - b - 1;
b[cnt + 1] = b[cnt] + 1;
build(1, 1, cnt);
ll sum = 0;
for(int i = 1; i <= n; ++i)
{
sum += R[i] - L[i] + 1;
L[i] = lower_bound(b + 1, b + cnt + 1, L[i]) - b ;
R[i] = lower_bound(b + 1, b + cnt + 1, R[i] + 1) - b ;
update(1,1,cnt, L[i], R[i] - 1);
cout << query(1,1,cnt,(sum - 1)/ 2 + 1) << '\n';
}

}