P2839 [国家集训队]middle 题解
AC 的第一道紫题,以此留念。
Sol
若直接上手求最大中位数,会发现中位数不仅受相对大小影响,还会受数量的影响,因此难以处理。
因此考虑二分答案。
我们设二分出的中位数为
因为
考虑最大化
发现区间最大前缀、后缀与区间和均可用线段树维护,但问题是因为要求关于所有
考虑将原数组排序,会发现当第
时间复杂度
代码
#include<iostream>
#include<algorithm>
using namespace std;
#define ll long long
const int MAXN = 2e4 + 10;
struct item
{
ll num, val;
}a[MAXN];
struct segmentTree
{
ll ls, rs, lm, rm, sum;
}tree[MAXN << 6];
ll lsh[MAXN], pos[MAXN], t[MAXN], root[MAXN], q[4];
ll n, Q, ans;
bool cmp(item x, item y) { return x.val < y.val; }
ll cnt;
void push_up(ll x)
{
tree[x].sum = tree[tree[x].ls].sum + tree[tree[x].rs].sum;
tree[x].lm = max(tree[tree[x].ls].lm, tree[tree[x].ls].sum + tree[tree[x].rs].lm);
tree[x].rm = max(tree[tree[x].rs].rm, tree[tree[x].rs].sum + tree[tree[x].ls].rm);
}
void build(ll &x, ll l, ll r)
{
x = ++cnt;
if (l == r)
{
tree[x].sum = 1, tree[x].lm = 1, tree[x].rm = 1;
return;
}
ll mid = (l + r) >> 1;
build(tree[x].ls, l, mid);
build(tree[x].rs, mid + 1, r);
push_up(x);
}
void change(ll &now, ll last, ll l, ll r, ll p)
{
now = ++cnt;
if (l == r)
{
tree[now].sum = -1, tree[now].lm = -1, tree[now].rm = -1;
return;
}
tree[now].ls = tree[last].ls;
tree[now].rs = tree[last].rs;
ll mid = (l + r) >> 1;
if (p <= mid) change(tree[now].ls, tree[last].ls, l, mid, p);
else change(tree[now].rs, tree[last].rs, mid + 1, r, p);
push_up(now);
}
ll query_sum(ll x, ll l, ll r, ll L, ll R)
{
if (L <= l && R >= r)
return tree[x].sum;
else if (L <= r && R >= l)
{
ll mid = (l + r) >> 1;
return query_sum(tree[x].ls, l, mid, L, R) + query_sum(tree[x].rs, mid + 1, r, L, R);
}
else return 0;
}
ll query_lmax(ll x, ll l, ll r, ll L, ll R)
{
if (L <= l && R >= r)
return tree[x].lm;
else if (L <= r && R >= l)
{
ll mid = (l + r) >> 1;
return max(query_lmax(tree[x].ls, l, mid, L, R),
query_sum(tree[x].ls, l, mid, L, R) + query_lmax(tree[x].rs, mid + 1, r, L, R));
}
else return 0;
}
ll query_rmax(ll x, ll l, ll r, ll L, ll R)
{
if (L <= l && R >= r)
return tree[x].rm;
else if (L <= r && R >= l)
{
ll mid = (l + r) >> 1;
return max(query_rmax(tree[x].rs, mid + 1, r, L, R),
query_sum(tree[x].rs, mid + 1, r, L, R) + query_rmax(tree[x].ls, l, mid, L, R));
}
else return 0;
}
ll res;
void my_unique()
{
res = 0;
sort(a + 1, a + n + 1, cmp);
for (int i = 1; i <= n; i++)
if (a[i].val == a[i - 1].val && i > 1)
lsh[a[i].num] = res;
else
lsh[a[i].num] = ++res, pos[res] = i, t[res] = a[i].val;
}
int main()
{
cin >> n;
for (int i = 1; i <= n; i++)
cin >> a[i].val, a[i].num = i;
my_unique();
build(root[1], 1, n);
for (int i = 2; i <= n; i++)
change(root[i], root[i - 1], 1, n, a[i - 1].num);
cin >> Q;
while (Q--)
{
ll a, b, c, d;
cin >> a >> b >> c >> d;
q[0] = (a + ans) % n + 1; q[1] = (b + ans) % n + 1;
q[2] = (c + ans) % n + 1; q[3] = (d + ans) % n + 1;
sort(q, q + 4);
ll l = 1, r = res;
while (l <= r)
{
ll mid = (l + r) >> 1;
ll tot1 = query_sum(root[pos[mid]], 1, n, q[1], q[2]);
ll tot2 = query_rmax(root[pos[mid]], 1, n, q[0], q[1] - 1);
ll tot3 = query_lmax(root[pos[mid]], 1, n, q[2] + 1, q[3]);
if (tot1 + tot2 + tot3 >= 0)
l = mid + 1, ans = t[mid];
else
r = mid - 1;
}
cout << ans << endl;
}
return 0;
}