luogu2839题解
题目分析
本题的暴力做法就是枚举两端的端点,然后求中位数取最大值。
不用说必然超时。
可以从枚举中看出,无论如何,考虑中位数的时候一定会选 \(\left[b+1,c-1\right]\) 中的数(除非 \(\left[b+1,c-1\right]\) 为空集,后面要特判此情况)。
本题需查找最大的中位数,也许可以二分?
假定选择一个区间 \(\left[x,y\right]\),和一个数 \(k\),需判断中位数是否大于等于 \(k\)。
我们可以得知这个区间的长度从而求得中位数的位置,然后根据小于 \(k\) 的数的数量或是大于等于 \(k\) 的数的数量可以判断该问题。
所以这个问题就转化成了求大于等于 \(k\) 的数的数量和小于 \(k\) 的数的数量哪个多,如果前者多则可以取大于等于 \(k\) 的中位数,否则反之。
我们可以给大于等于 \(k\) 的数赋一个 \(1\) 的权值,给小于 \(k\) 的数赋一个 \(-1\) 的权值,这样就是把所有权值加起来,如结果大于等于 \(0\) 则可以取到大于等于 \(k\) 的中位数,否则反之。
容易发现这个问题被转化成了一个最大子区间的问题,不过区间必须包含 \(\left[b+1,c-1\right]\) 这段区间,求这段区间的和,然后取一个左端点 \(l\in\left[a,b\right]\) 使得 \(\left[l,b\right]\) 的区间和最大,取一个右端点 \(r\in\left[c,d\right]\) 使得 \(\left[c,r\right]\) 的区间和最大,将三段区间的和加起来判断是否大于等于 \(0\) 即可完成判断。
对于这个序列,我们可以建一棵可持久化线段树,对于每个值建一棵线段树。
给这个序列另开个数组排个序按顺序建。
第一棵树对应最小的数,所以全部赋 \(1\) 即可。
我们可以根据上一棵树来进行单点修改完成建树。
像刚才那样说的,修改的点赋 \(-1\) 就好了。
如果需要由上一棵线段树修改至少两个点且直接在上一棵线段树上进行单点修改的话,并不能将所有修改操作继承下来,因为每次都是在上一棵线段树上操作,这样修改下来最多只能改变一个点。(就是上一次操作本来已经作为一棵树修改好了,但是第二次操作会重新在上一棵树的基础上修改一个点替换掉这棵树,修改操作不能这样累加起来)
那肯定在修改好的树上接着修改啊。
于是后面的修改在这棵树上修改即可,由于修改的时候只有模拟指针的变量变化,原来的信息是不会出错的,所以修改出来的树是正常的。
为方便操作减少特判,先直接将这个树的根指向上一棵树的根,然后接着修改即可。
代码如下。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int MAXN=2e4+10;
int x,n,Q,a[MAXN],q[6],root[MAXN],b[MAXN],tot;
vector<int> locp[MAXN];
struct SegmentTreeNode{
int l,r,lson,rson;
int sum,lsum,rsum;
}tr[MAXN*25];
int newnode(){
return ++tot;
}
void pushup(int id){
tr[id].sum=tr[tr[id].lson].sum+tr[tr[id].rson].sum;
tr[id].lsum=max(tr[tr[id].lson].lsum,tr[tr[id].lson].sum+tr[tr[id].rson].lsum);
tr[id].rsum=max(tr[tr[id].rson].rsum,tr[tr[id].lson].rsum+tr[tr[id].rson].sum);
}
int build(int l,int r){
int p=newnode();
tr[p].l=l;tr[p].r=r;
if(l==r){
tr[p].lsum=tr[p].rsum=tr[p].sum=1;
return p;
}
int mid=(l+r)>>1;
tr[p].lson=build(l,mid);
tr[p].rson=build(mid+1,r);
pushup(p);
return p;
}
void change(int p1,int &p2,int loc){//默认修改为-1
p2=newnode();
tr[p2].l=tr[p1].l;tr[p2].r=tr[p1].r;
if(tr[p1].l==tr[p1].r){
tr[p2].lsum=tr[p2].rsum=tr[p2].sum=-1;
return;
}
int mid=(tr[p1].l+tr[p1].r)>>1;
if(loc<=mid){
change(tr[p1].lson,tr[p2].lson,loc);
tr[p2].rson=tr[p1].rson;
}else{
tr[p2].lson=tr[p1].lson;
change(tr[p1].rson,tr[p2].rson,loc);
}
pushup(p2);
}
SegmentTreeNode query(int p,int l,int r){
if(tr[p].l==l&&tr[p].r==r)return tr[p];
if(r<=tr[tr[p].lson].r)return query(tr[p].lson,l,r);
else if(tr[tr[p].rson].l<=l)return query(tr[p].rson,l,r);
else{
SegmentTreeNode ln=query(tr[p].lson,l,tr[tr[p].lson].r),rn=query(tr[p].rson,tr[tr[p].rson].l,r),rt;
rt.lsum=max(ln.lsum,ln.sum+rn.lsum);
rt.rsum=max(ln.rsum+rn.sum,rn.rsum);
rt.sum=ln.sum+rn.sum;
return rt;
}
}
int read(){
int f=1,x=0;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+(ch^48);ch=getchar();}
return f*x;
}
int main(){
n=read();
for(int i=0;i<n;++i){
b[i]=a[i]=read();
}
sort(b,b+n);
int bn=unique(b,b+n)-b;
for(int i=0;i<n;++i){
locp[lower_bound(b,b+bn,a[i])-b].push_back(i);
}
root[0]=build(0,n-1);
for(int i=1;i<bn;++i){
root[i]=root[i-1];
for(int j:locp[i-1]){
change(root[i],root[i],j);
}
}
Q=read();
while(Q){
--Q;
for(int i=0;i<4;++i){
q[i]=(read()+x)%n;
}
sort(q,q+4);
int l=0,r=bn-1;
while(l<r){
int mid=(l+r+1)>>1;
int mval=query(root[mid],q[0],q[1]).rsum;
if((q[1]+1)<=(q[2]-1))mval+=query(root[mid],q[1]+1,q[2]-1).sum;
mval+=query(root[mid],q[2],q[3]).lsum;
if(mval>=0)l=mid;
else r=mid-1;
}
x=b[l];
printf("%d\n",b[l]);
}
return 0;
}