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

猜你喜欢

转载自blog.csdn.net/xyz32768/article/details/84839290
今日推荐