传送门

主席树大好题……这道题让主席树不仅停留在了区间第k大上,而是让它能执行像线段树一样的操作。

首先我们先说点套路的事。求中位数有一个二分法,就是每次二分答案,把大于等于当前二分的数设为1,小于的设为-1,之后我们只要看和是否大于0就能判断限制二分的值是大是小。然后虽然区间是不确定的,但是我们要求最大的中位数,只要取中间固定的一段+左区间最大后缀+右区间最大前缀判断即可。

不过这样的话每次暴力的时候,查询是(O(nlogn))的,再套上查询次数会超时。如何优化一下呢?如果我们用线段树对于每一个二分的值,都维护一下它当前的值和最大前后缀,这样的话我们就只需要(O(log^2n))的复杂度来完成一次查询。但是这样会(MLE)

然后在每两棵树之间,其实只有(i-1)发生了变化,从1变成了-1.这样的话我们就可以把这些线段树建成主席树,用主席树来实现线段树一样的操作就好了。注意这道题是把以当前这个数的值为根,以位置为值域,注意访问区间不要开小(我就因为这个大数据(MLE)),最开始建树的地方比较重要,我们需要开一个(vector)来把数的值一样,多次出现的数的位置都存起来,然后按照值的大小按顺序修改即可。

看一下代码。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<cmath>
#include<set>
#include<vector>
#include<map>
#include<queue>
#define rep(i,a,n) for(int i = a;i <= n;i++)
#define per(i,n,a) for(int i = n;i >= a;i--)
#define enter putchar('
')
#define fr friend inline
#define y1 poj
#define mp make_pair
#define pr pair<int,int>
#define fi first
#define sc second
#define pb push_back
#define I puts("Oops")

using namespace std;
typedef long long ll;
const int M = 20005;
const int N = 1000005;
const int INF = 1000000009;
const double eps = 1e-7;

int read()
{
    int ans = 0,op = 1;char ch = getchar();
    while(ch < '0' || ch > '9') {if(ch == '-') op = -1;ch = getchar();}
    while(ch >= '0' && ch <= '9') ans = ans * 10 + ch - '0',ch = getchar();
    return ans * op;
}

struct node
{
   int ls,rs,v,lv,rv;
}t[N<<2];

int n,Q,a[M],b[M],root[M],q[6],x,y,z,e,tot,la,L,R,g,idx;
vector <int> V[M];

void update(int p)
{
   t[p].v = t[t[p].ls].v + t[t[p].rs].v;
   t[p].lv = max(t[t[p].ls].lv,t[t[p].ls].v + t[t[p].rs].lv);
   t[p].rv = max(t[t[p].rs].rv,t[t[p].rs].v + t[t[p].ls].rv);
}

void build(int &p,int l,int r)
{
   if(!p) p = ++idx;
   if(l == r) {t[p].v = t[p].lv = t[p].rv = 1;return;}
   int mid = (l+r) >> 1;
   build(t[p].ls,l,mid),build(t[p].rs,mid+1,r);
   update(p);
}

void modify(int old,int &p,int l,int r,int val)
{
   p = ++idx;
   t[p] = t[old];
   if(l == r) {t[p].v = t[p].lv = t[p].rv = -1;return;}
   int mid = (l+r) >> 1;
   if(val <= mid) modify(t[old].ls,t[p].ls,l,mid,val);
   else modify(t[old].rs,t[p].rs,mid+1,r,val);
   update(p);
}

int query(int p,int l,int r,int kl,int kr)
{
   //I;
   if(kr < kl) return 0;
   if(l == kl && r == kr) return t[p].v;
   int mid = (l+r) >> 1;
   if(kr <= mid) return query(t[p].ls,l,mid,kl,kr);
   else if(kl > mid) return query(t[p].rs,mid+1,r,kl,kr);
   else return query(t[p].ls,l,mid,kl,mid) + query(t[p].rs,mid+1,r,mid+1,kr);
}

int ask(int p,int l,int r,int kl,int kr,bool flag)//1 left , 0 right
{
   //I;
   //printf("#%d %d %d %d
",l,r,kl,kr);
   if(l == kl && r == kr) return flag ? t[p].lv : t[p].rv;
   int mid = (l+r) >> 1;
   if(kr <= mid) return ask(t[p].ls,l,mid,kl,kr,flag);
   else if(kl > mid) return ask(t[p].rs,mid+1,r,kl,kr,flag);
   else
   {
      int cur1 = 0,cur2 = 0;
      cur1 = flag ? ask(t[p].ls,l,mid,kl,mid,flag) : ask(t[p].rs,mid+1,r,mid+1,kr,flag);
      if(flag) cur2 = query(t[p].ls,l,mid,kl,mid) + ask(t[p].rs,mid+1,r,mid+1,kr,flag);
      else cur2 = query(t[p].rs,mid+1,r,mid+1,kr) + ask(t[p].ls,l,mid,kl,mid,flag);
      return max(cur1,cur2);
   }
}

bool judge(int x)
{
   int cur1 = query(root[x],1,n,q[2]+1,q[3]-1);
   int cur2 = ask(root[x],1,n,q[1],q[2],0);
   int cur3 = ask(root[x],1,n,q[3],q[4],1);
   return cur1 + cur2 + cur3 >= 0;
}

int main()
{
   n = read(),build(root[0],1,n);
   rep(i,1,n) a[i] = b[i] = read();
   sort(b+1,b+1+n),tot = unique(b+1,b+1+n) - b - 1;
   rep(i,1,n) a[i] = lower_bound(b+1,b+1+tot,a[i]) - b,V[a[i]].pb(i);
   rep(i,1,tot)
   {
      root[i] = root[i-1];
      rep(j,0,(int)V[i-1].size()-1) modify(root[i],root[i],1,n,V[i-1][j]);
   }
   Q = read();
   rep(i,1,Q)
   {
      rep(j,1,4) q[j] = read(),q[j] = (q[j] + la) % n + 1;
      sort(q+1,q+5),L = 1,R = tot;
      while(L < R)
      {
	 int mid = (L+R+1) >> 1;
	 if(judge(mid)) L = mid;
	 else R = mid - 1;
      }
      la = b[L];
      printf("%d
",la);
   }
   return 0;
}