[国家集训队]middle

嘟嘟嘟


有谁能想到这题会用到主席树呢?(不愧是WJMZBMR出的题)


首先考虑如果区间是固定的话,中位数该怎么求。
没错,二分。如果大于当前二分值\(mid\)的数比小于\(mid\)的数多,说明\(mid\)还可以再变大,向右二分;否则向左二分。
如果我们把小于\(mid\)的数都标记成\(-1\),大于的标记成\(1\),那么只用判断这个区间的和是否\(\geqslant 0\)就行了。


但现在区间不固定。首先\([b + 1, c - 1]\)是一定要选的。对于\([a, b]\)和\([c, d]\),因为要让中位数尽量大,所以应该选\([a, b]\)的最大后缀和以及\([c, d]\)的最大前缀和。


主要思路就是这些,但单次查询的复杂度是\(O(n \log{n})\)的,过不了。得想办法优化。


如果对每一个二分的值建一棵区间线段树(这里的二分在序列中的值中进行就行,而不是\(1\)到\(1e9\),所以只用建\(n\)棵),把小于他的都标记成\(1\),大于标记成\(-1\),那么每一次查询就能达到\(O(\log ^ 2{n})\)了。
但是很显然这样空间开不下,而且预处理复杂度过高。所以现在得想办法减少预处理的时间。
如果把序列中的数排一个序,会发现对于相邻的两个不一样的数(因为数字可能有重),建的线段树只有一处不一样,而这一处不一样只会导致线段树中的一条链改变。所以我们只要单独把这条链提建出来就行了。
然后就会发现这其实就是一棵主席树呀。


于是这题就写完了。

#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<vector>
#include<stack>
#include<queue>
using namespace std;
#define enter puts("") 
#define space putchar(' ')
#define Mem(a, x) memset(a, x, sizeof(a))
#define rg register
typedef long long ll;
typedef double db;
const int INF = 0x3f3f3f3f;
const db eps = 1e-8;
const int maxn = 2e4 + 5;
const int maxt = 2e6 + 5;
inline ll read()
{
  ll ans = 0;
  char ch = getchar(), last = ' ';
  while(!isdigit(ch)) {last = ch; ch = getchar();}
  while(isdigit(ch)) {ans = (ans << 1) + (ans << 3) + ch - '0'; ch = getchar();}
  if(last == '-') ans = -ans;
  return ans;
}
inline void write(ll x)
{
  if(x < 0) x = -x, putchar('-');
  if(x >= 10) write(x / 10);
  putchar(x % 10 + '0');
}

int n, _n, m, a[maxn], b[maxn], q[4];
vector<int> v[maxn];

struct Tree
{
  int ls, rs;
  int sum, lmax, rmax;
}t[maxt];
int root[maxn], cnt = 0;
void pushup(int now)
{
  t[now].sum = t[t[now].ls].sum + t[t[now].rs].sum;
  t[now].lmax = max(t[t[now].ls].lmax, t[t[now].ls].sum + t[t[now].rs].lmax);
  t[now].rmax = max(t[t[now].rs].rmax, t[t[now].rs].sum + t[t[now].ls].rmax);
}
void build(int& now, int l, int r)
{
  if(!now) now = ++cnt;
  if(l == r) {t[now].sum = t[now].lmax = t[now].rmax = 1; return;}
  int mid = (l + r) >> 1;
  build(t[now].ls, l, mid);
  build(t[now].rs, mid + 1, r);
  pushup(now);
}
void insert(int old, int& now, int l, int r, int id)
{
  t[now = ++cnt] = t[old];
  if(l == r) {t[now].sum = t[now].lmax = t[now].rmax = -1; return;}
  int mid = (l + r) >> 1;
  if(id <= mid) insert(t[old].ls, t[now].ls, l, mid, id);
  else insert(t[old].rs, t[now].rs, mid + 1, r, id);
  pushup(now);
}
int querySum(int now, int l, int r, int L, int R)
{
  if(R < L) return 0;
  if(l == L && r == R) return t[now].sum;
  int mid = (l + r) >> 1;
  if(R <= mid) return querySum(t[now].ls, l, mid, L, R);
  else if(L > mid) return querySum(t[now].rs, mid + 1, r, L, R);
  else return querySum(t[now].ls, l, mid, L, mid) + querySum(t[now].rs, mid + 1, r, mid + 1, R);
}
int queryL(int now, int l, int r, int L, int R)
{
  if(l == L && r == R) return t[now].lmax;
  int mid = (l + r) >> 1;
  if(R <= mid) return queryL(t[now].ls, l, mid, L, R);
  else if(L > mid) return queryL(t[now].rs, mid + 1, r, L, R);
  else
    {
      int ret1 = queryL(t[now].ls, l, mid, L, mid);
      int ret2 = querySum(t[now].ls, l, mid, L, mid) + queryL(t[now].rs, mid + 1, r, mid + 1, R);
      return max(ret1, ret2);
    }
}
int queryR(int now, int l, int r, int L, int R)
{
  if(l == L && r == R) return t[now].rmax;
  int mid = (l + r) >> 1;
  if(R <= mid) return queryR(t[now].ls, l, mid, L, R);
  else if(L > mid) return queryR(t[now].rs, mid + 1, r, L, R);
  else
    {
      int ret1 = queryR(t[now].rs, mid + 1, r, mid + 1, R);
      int ret2 = querySum(t[now].rs, mid + 1, r, mid + 1, R) + queryR(t[now].ls, l, mid, L, mid);
      return max(ret1, ret2);
    }
}

bool judge(int x)
{
  int ans1 = querySum(root[x], 1, n, q[1] + 1, q[2] - 1);
  int ans2 = queryR(root[x], 1, n, q[0], q[1]);
  int ans3 = queryL(root[x], 1, n, q[2], q[3]);
  return ans1 + ans2 + ans3 >= 0;
}
int solve()
{
  int L = 1, R = _n;
  while(L < R)
    {
      int mid = (L + R + 1) >> 1;
      if(judge(mid)) L = mid;
      else R = mid - 1;
    }
  return L;
}

int main()
{
  n = read();
  build(root[0], 1, n);
  for(int i = 1; i <= n; ++i) a[i] = b[i] = read();
  sort(b + 1, b + n + 1);
  _n = unique(b + 1, b + n + 1) - b - 1;
  for(int i = 1; i <= n; ++i)
    {
      a[i] = lower_bound(b + 1, b + _n + 1, a[i]) - b;
      v[a[i]].push_back(i);
    }
  for(int i = 1; i <= _n; ++i)
    {
      root[i] = root[i - 1];
      for(int j = 0; j < (int)v[i - 1].size(); ++j)
	insert(root[i], root[i], 1, n, v[i - 1][j]);
    }
  m = read();
  for(int i = 1, ans = 0; i <= m; ++i)
    {
      for(int j = 0; j < 4; ++j) q[j] = read();
      for(int j = 0; j < 4; ++j) q[j] = (q[j] + ans) % n + 1;
      sort(q, q + 4);
      ans = b[solve()];
      write(ans), enter;
    }
  return 0;
}
上一篇:如何帮助“被遗忘的中间人群”充分发挥潜能?


下一篇:java实现二分查找及其变种