2653: middle
Time Limit: 20 Sec Memory Limit: 512 MB
Submit: 984 Solved: 571
[Submit][Status][Discuss]
Description
一个长度为n的序列a,设其排过序之后为b,其中位数定义为b[n/2],其中a,b从0开始标号,除法取下整。
给你一个长度为n的序列s。
回答Q个这样的询问:s的左端点在[a,b]之间,右端点在[c,d]之间的子序列中,最大的中位数。
其中a < b < c < d。
位置也从0开始标号。
我会使用一些方式强制你在线。
Input
第一行序列长度n。
接下来n行按顺序给出a中的数。
接下来一行Q。
然后Q行每行a,b,c,d,我们令上个询问的答案是x(如果这是第一个询问则x=0)。
令数组q={(a+x)%n,(b+x)%n,(c+x)%n,(d+x)%n}。
将q从小到大排序之后,令真正的要询问的a=q[0],b=q[1],c=q[2],d=q[3]。
输入保证满足条件。
Output
Q行依次给出询问的答案。
Sample Input
5
170337785
271451044
22430280
969056313
206452321
3
3 1 0 2
2 3 1 4
3 1 4 0
Sample Output
271451044
271451044
969056313
HINT
0:n,Q<=100
1,…,5:n<=2000
0,…,19:n<=20000,Q<=25000
题目中要求的是中位数,中位数这个东西看上去很像二分对吧,那对于一个询问,首先二分答案。怎样判断答案是否正确呢?
假如当前二分的数是k,那么将n个数中所有>=k的全赋成1,< k的赋成-1,这样就是相当于求一段最大的连续字段和,看看能否>=0。直到找到一个k,使得k的时候>=0,k+1的时候<0,那么这个k就是我们要的答案。
但是对于每一个二分的值,不能暴力修改线段树中的每一个元素。所以我们就需要用到主席树。
首先将输入的数排序,我们将主席树的外层当做数的权值,内层是数的序号。开始的时候建议可完整的数,所有节点都是1,。然后将排完序的数组依次添加到主席树中去.。这样查询一个数k的时候,只需要找到对应的root[k]中去找就行了。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define mid (L+R)/2
const int N=20010;
const int M=800010;
struct S{int No,v;}a[N];
int n,m,siz,q[5],root[N],l[M],r[M],sum[M],lmax[M],rmax[M];
inline bool cmp(S x,S y){return x.v<y.v;}
inline void update(int k){
sum[k]=sum[l[k]]+sum[r[k]];
lmax[k]=max(lmax[l[k]],sum[l[k]]+lmax[r[k]]);
rmax[k]=max(rmax[r[k]],sum[r[k]]+rmax[l[k]]);
}
inline void build(int L,int R,int &k){
k=++siz;
if(L==R){
lmax[k]=rmax[k]=sum[k]=1;
return ;
}
build(L,mid,l[k]);
build(mid+1,R,r[k]);
update(k);
}
inline void insert(int L,int R,int x,int &y,int z,int value){
y=++siz;
if(L==R){
lmax[y]=rmax[y]=sum[y]=value;
return ;
}
l[y]=l[x];r[y]=r[x];
if(z<=mid) insert(L,mid,l[x],l[y],z,value);
else insert(mid+1,R,r[x],r[y],z,value);
update(y);
}
inline int query_sum(int L,int R,int k,int x,int y){
int tot=0;
if(x<=L&&y>=R) return sum[k];
if(x<=mid) tot+=query_sum(L,mid,l[k],x,y);
if(y>mid) tot+=query_sum(mid+1,R,r[k],x,y);
return tot;
}
inline int query_lmax(int L,int R,int k,int x,int y){
if(x==L&&y==R) return lmax[k];
if(x>mid) return query_lmax(mid+1,R,r[k],x,y);
else if(y<=mid) return query_lmax(L,mid,l[k],x,y);
else return max(query_lmax(L,mid,l[k],x,mid),query_sum(L,mid,l[k],x,mid)+query_lmax(mid+1,R,r[k],mid+1,y));
}
inline int query_rmax(int L,int R,int k,int x,int y){
if(x==L&&y==R) return rmax[k];
if(x>mid) return query_rmax(mid+1,R,r[k],x,y);
else if(y<=mid) return query_rmax(L,mid,l[k],x,y);
else return max(query_rmax(mid+1,R,r[k],mid+1,y),query_sum(mid+1,R,r[k],mid+1,y)+query_rmax(L,mid,l[k],x,mid));
}
inline bool check(int x){
int tot=0;
tot=query_sum(1,n,root[x],q[2],q[3]);
//if(x==8) cout<<tot<<"~"<<endl;
if(q[1]<=q[2]-1) tot+=max(0,query_rmax(1,n,root[x],q[1],q[2]-1));
//if(x==8) cout<<tot<<' '<<q[3]<<' '<<q[4]<<"~~"<<endl;
if(q[3]+1<=q[4]) tot+=max(0,query_lmax(1,n,root[x],q[3]+1,q[4]));
//cout<<x<<' '<<tot<<endl;
return tot>=0;
}
int main(){
//freopen("input.in","r",stdin);
int i,j,ans=0;
scanf("%d",&n);
for(i=1;i<=n;++i){
a[i].No=i;
scanf("%d",&a[i].v);
}
sort(a+1,a+n+1,cmp);
//for(i=1;i<=n;++i) cout<<a[i].v<<' ';
//cout<<endl;
build(1,n,root[1]);
for(i=2;i<=n;++i) insert(1,n,root[i-1],root[i],a[i-1].No,-1);
/*for(i=1;i<=n;++i) cout<<sum[root[i]]<<' ';
while(1);*/
scanf("%d",&m);
while(m--){
for(i=1;i<=4;++i) scanf("%d",&q[i]),q[i]=(q[i]+ans)%n+1;
sort(q+1,q+5);
//for(i=1;i<=4;++i) cout<<q[i]<<' ';
//cout<<endl;
//while(1);
int L=1,R=n+1;ans=1;
//cout<<m<<"~"<<endl;
//if(m==0) cout<<check(5)<<"~"<<endl;
while(L<R){
//cout<<L<<' '<<R<<' '<<mid<<"!"<<endl;
if(check(mid)) ans=max(ans,mid),L=mid+1;
else R=mid;
}
ans=a[ans].v;
printf("%d\n",ans);
}
}