题目:
分析:
记
s
i
s_i
si表示原序列中第
i
i
i大的数。
考虑对于任意一个区间
[
a
,
b
]
[a,b]
[a,b],设它的中位数为
s
m
s_m
sm,那么这个区间内大于等于
s
m
s_m
sm的数和小于
s
m
s_m
sm的数的数量要么相等,要么小于比大于等于多一个。后一种情况当且仅当
s
m
∈
[
a
,
b
]
s_m\in [a,b]
sm∈[a,b]且序列长度为奇数。
考虑如果已知一个数
s
i
s_i
si,如何判断是否存在区间
[
e
,
f
]
[e,f]
[e,f](
e
∈
[
a
,
b
]
,
f
∈
[
c
,
d
]
e\in [a,b],f\in [c,d]
e∈[a,b],f∈[c,d])使
[
e
,
f
]
[e,f]
[e,f]的中位数大于等于
s
i
s_i
si呢(显然满足条件的
i
i
i是单调的,可以二分)?
对于
s
i
s_i
si,记所有小于它的数为
−
1
-1
−1,大于等于它的数为
1
1
1,那么如果能找到一个符合条件的区间
[
e
,
f
]
[e,f]
[e,f]使这段区间的和大于等于
0
0
0说明最大中位数大于等于
s
i
s_i
si,记录答案并尝试更大的
i
i
i;否则不符合条件,需要调小
i
i
i。
此时问题变成了已知一个
1
1
1和
−
1
-1
−1组成的序列,求一个
[
e
,
f
]
[e,f]
[e,f]使它的区间和最大。把
[
e
,
f
]
[e,f]
[e,f]拆成三部分:必选的
(
b
,
c
)
(b,c)
(b,c),在
[
a
,
b
]
[a,b]
[a,b]中选一段后缀的
[
e
,
b
]
[e,b]
[e,b]和在
[
c
,
d
]
[c,d]
[c,d]中选一段前缀的
[
c
,
f
]
[c,f]
[c,f]。这个可以用线段树解决,方法类似【Vijos1083/BZOJ1756】小白逛公园(线段树),查询
(
b
,
c
)
(b,c)
(b,c)的
v
a
l
val
val,
[
a
,
b
]
[a,b]
[a,b]的
r
m
rm
rm和
[
c
,
d
]
[c,d]
[c,d]的
l
m
lm
lm。
s
i
s_i
si对应的线段树和
s
i
−
1
s_{i-1}
si−1对应的线段树的区别仅仅是把
(
i
−
1
)
(i-1)
(i−1)位置的
1
1
1变成了
−
1
-1
−1,因此可以用主席树维护节省空间。
代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;
namespace zyt
{
const int N = 2e4 + 10, Q = 2.5e4 + 10;
int n, q, tmp[N], head[N];
pair<int, int> arr[N];
namespace Chairman_Tree
{
const int SUM = 0, SUML = 1, SUMR = 2;
struct node
{
int sum, suml, sumr, s[2];
node(): sum(0), suml(0), sumr(0)
{
s[0] = s[1] = 0;
}
}tree[N * 20];
int cnt;
inline void update(node &now, const node <, const node &rt)
{
now.suml = now.sumr = 0;
now.sum = lt.sum + rt.sum;
now.suml = max(lt.suml, lt.sum + rt.suml);
now.sumr = max(rt.sumr, rt.sum + lt.sumr);
}
inline void update(const int rot)
{
update(tree[rot], tree[tree[rot].s[0]], tree[tree[rot].s[1]]);
}
int build(const int lt, const int rt, const int val)
{
int rot = ++cnt;
if (lt == rt)
{
tree[rot].sum = tree[rot].suml = tree[rot].sumr = -1 + 2 * (tmp[lt] >= val);
return rot;
}
int mid = (lt + rt) >> 1;
tree[rot].s[0] = build(lt, mid, val);
tree[rot].s[1] = build(mid + 1, rt, val);
update(rot);
return rot;
}
int change(const int pre, const int lt, const int rt, const int pos, const int x)
{
int rot = ++cnt;
tree[rot] = tree[pre];
if (lt == rt)
{
tree[rot].sum = x;
tree[rot].suml = x;
tree[rot].sumr = x;
return rot;
}
int mid = (lt + rt) >> 1;
if (pos <= mid)
tree[rot].s[0] = change(tree[pre].s[0], lt, mid, pos, x);
else
tree[rot].s[1] = change(tree[pre].s[1], mid + 1, rt, pos, x);
update(rot);
return rot;
}
node query(const int rot, const int lt, const int rt, const int ls, const int rs)
{
if (ls <= lt && rt <= rs)
return tree[rot];
int mid = (lt + rt) >> 1;
if (rs <= mid)
return query(tree[rot].s[0], lt, mid, ls, rs);
else if (ls > mid)
return query(tree[rot].s[1], mid + 1, rt, ls, rs);
else
{
node tmp[2] = {query(tree[rot].s[0], lt, mid, ls, rs),
query(tree[rot].s[1], mid + 1, rt, ls, rs)};
node ans;
update(ans, tmp[0], tmp[1]);
return ans;
}
}
int query(const int rot, const int ls, const int rs, const int type)
{
node ans = query(rot, 0, n - 1, ls, rs);
switch (type)
{
case SUM:
return ans.sum;
case SUML:
return ans.suml;
case SUMR:
return ans.sumr;
}
}
}
int work()
{
using namespace Chairman_Tree;
ios::sync_with_stdio(false);
cin.tie(0);
cin >> n;
for (int i = 0; i < n; i++)
cin >> arr[i].first, arr[i].second = i, tmp[i] = arr[i].first;
sort(arr, arr + n);
head[0] = build(0, n - 1, arr[0].first);
for (int i = 1; i < n; i++)
head[i] = change(head[i - 1], 0, n - 1, arr[i - 1].second, -1);
cin >> q;
int x = 0;
while (q--)
{
int in[4];
for (int i = 0; i < 4; i++)
cin >> in[i], in[i] = (in[i] + x) % n;
sort(in, in + 4);
int l = 0, r = n - 1, ans;
while (l <= r)
{
int mid = (l + r) >> 1;
int tmp = query(head[mid], in[0], in[1], SUMR) + query(head[mid], in[2], in[3], SUML);
if (in[1] + 1 <= in[2] - 1)
tmp += query(head[mid], in[1] + 1, in[2] - 1, SUM);
if (tmp >= 0)
l = mid + 1, ans = mid;
else
r = mid - 1;
}
cout << arr[ans].first << '\n';
x = arr[ans].first;
}
return 0;
}
}
int main()
{
return zyt::work();
}