[BZOJ2653]middle(二分答案 + 主席樹)
阿新 • • 發佈:2018-12-08
Address
Solution
- 很不錯的題
- 求某個排名的數的最值是一個經典的二分答案套路
- 方法為:二分答案 之後,把所有數按照與 的大小關係變成 和 進行判斷
- 而本題也可以先離散化權值之後二分答案
- 把所有 的值變成 , 的值變成
- 判斷左端點在 內,右端點在 內的所有區間中,是否存在一個區間滿足 的個數 的個數
- 還是不好算,於是我們考慮把 的值變成
- 判斷左端點在 內,右端點在 內的所有區間中,是否存在一個區間的和
- 考慮分成三段
- (1) 的最大字尾和
- (2) 區間的和
- (3) 的最大字首和
- 把這三部分加起來,就是左端點在 內且右端點在 內的所有區間中,最大的區間和
- 設有 ( 為離散化後序列中不同數的個數)棵線段樹,分別為第 棵到第 棵,第 棵線段樹儲存當 的數改成 , 的數改成 後的區間和、最大字首和、最大字尾和
- 那麼查詢(1)(3)就只需要在第 棵線段樹上查詢下區間最大字尾 / 字首和即可
Code
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define For(i, a, b) for (i = a; i <= b; i++)
inline int read()
{
int res = 0; bool bo = 0; char c;
while (((c = getchar()) < '0' || c > '9') && c != '-');
if (c == '-') bo = 1; else res = c - 48;
while ((c = getchar()) >= '0' && c <= '9')
res = (res << 3) + (res << 1) + (c - 48);
return bo ? ~res + 1 : res;
}
template <class T>
inline T Max(const T &a, const T &b) {return a > b ? a : b;}
const int N = 2e4 + 5, L = 7e6 + 5;
int n, a[N], m, b[N], rt[N], q, tmp[4], lst, ToT, pos[N];
struct xpair
{
int x, y;
} qa[N];
struct tri
{
int sum, pre, suf;
friend inline tri operator + (tri a, tri b)
{
return (tri) {a.sum + b.sum, Max(a.pre, a.sum + b.pre),
Max(b.suf, b.sum + a.suf)};
}
};
inline bool comp(xpair a, xpair b)
{
return a.y < b.y;
}
struct node
{
int lc, rc; tri a;
} T[L];
void change(int l, int r, int pos, int v, int &p)
{
if (!p) p = ++ToT;
if (l == r) return (void) (T[p].a.sum = T[p].a.pre = T[p].a.suf = v);
int mid = l + r >> 1;
if (pos <= mid) change(l, mid, pos, v, T[p].lc);
else change(mid + 1, r, pos, v, T[p].rc);
T[p].a = T[T[p].lc].a + T[T[p].rc].a;
}
void changeof(int y, int &x, int l, int r, int pos, int v)
{
T[x = ++ToT] = T[y];
if (l == r) return (void) (T[x].a.sum = T[x].a.pre = T[x].a.suf = v);
int mid = l + r >> 1;
if (pos <= mid) changeof(T[y].lc, T[x].lc, l, mid, pos, v);
else changeof(T[y].rc, T[x].rc, mid + 1, r, pos, v);
T[x].a = T[T[x].lc].a + T[T[x].rc].a;
}
tri query(int l, int r, int s, int e, int p)
{
if (s > e) return (tri) {0, 0, 0};
if (l == s && r == e) return T[p].a;
int mid = l + r >> 1;
if (e <= mid) return query(l, mid, s, e, T[p].lc);
else if (s >= mid + 1) return query(mid + 1, r, s, e, T[p].rc);
else return query(l, mid, s, mid, T[p].lc)
+ query(mid + 1, r, mid + 1, e, T[p].rc);
}
bool check(int a, int b, int c, int d, int mid)
{
return query(1, n, a, b, rt[pos[mid - 1]]).suf
+ query(1, n, b + 1, c - 1, rt[pos[mid - 1]]).sum
+ query(1, n, c, d, rt[pos[mid - 1]]).pre >= 0;
}
int main()
{
int i;
n = read();
For (i, 1, n) a[i] = b[i] = read();
std::sort(b + 1, b + n + 1);
m = std::unique(b + 1, b + n + 1) - b - 1;
For (i, 1, n) a[i] = std::lower_bound(b + 1, b + m + 1, a[i]) - b;
For (i, 1, n) qa[i] = (xpair) {i, a[i]};
std::sort(qa + 1, qa + n + 1, comp);
For (i, 1, n) change(1, n, i, 1, rt[0]);
For (i, 1, n)
{
changeof(rt[i - 1], rt[i], 1, n, qa[i].x, -1);
pos[qa[i].y] = i;
}
q = read();
while (q--)
{
tmp[0] = (read() + lst) % n + 1;
tmp[1] = (read() + lst) % n + 1;
tmp[2] = (read() + lst) % n + 1;
tmp[3] = (read() + lst) % n + 1;
std::sort(tmp, tmp + 4);
int l = 1, r = m;
while (l <= r)
{
int mid = l + r >> 1;
if (check(tmp[0], tmp[1], tmp[2], tmp[3], mid)) l = mid + 1;
else r = mid - 1;
}
printf("%d\n", lst = b[r]);
}