1. 程式人生 > >[BZOJ2653]middle(二分答案 + 主席樹)

[BZOJ2653]middle(二分答案 + 主席樹)

Address

Solution

  • 很不錯的題
  • 求某個排名的數的最值是一個經典的二分答案套路
  • 方法為:二分答案 m i d mid 之後,把所有數按照與 m
    i d mid
    的大小關係變成 0 0 1
    1
    進行判斷
  • 而本題也可以先離散化權值之後二分答案 m i d mid
  • 把所有
    m i d \ge mid
    的值變成 1 1 < m i d <mid 的值變成 0 0
  • 判斷左端點在 [ a , b ] [a,b] 內,右端點在 [ c , d ] [c,d] 內的所有區間中,是否存在一個區間滿足 1 1 的個數 \ge 0 0 的個數
  • 還是不好算,於是我們考慮把 < m i d <mid 的值變成 1 -1
  • 判斷左端點在 [ a , b ] [a,b] 內,右端點在 [ c , d ] [c,d] 內的所有區間中,是否存在一個區間的和 0 \ge 0
  • 考慮分成三段
  • (1) [ a , b ] [a,b] 的最大字尾和
  • (2) [ b + 1 , c 1 ] [b+1,c-1] 區間的和
  • (3) [ c , d ] [c,d] 的最大字首和
  • 把這三部分加起來,就是左端點在 [ a , b ] [a,b] 內且右端點在 [ c , d ] [c,d] 內的所有區間中,最大的區間和
  • 設有 m + 1 m+1 m m 為離散化後序列中不同數的個數)棵線段樹,分別為第 0 0 棵到第 m m 棵,第 i i 棵線段樹儲存當 > i >i 的數改成 1 1 i \le i 的數改成 1 -1 後的區間和、最大字首和、最大字尾和
  • 那麼查詢(1)(3)就只需要在第 m i d 1 mid-1 棵線段樹上查詢下區間最大字尾 / 字首和即可
  • O ( n log 2 n ) O(n\log^2n)

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]);
	}