2653: middle
Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 2164 Solved: 1199
[ Submit][ Status][ Discuss]
Description
Input
Output
Q行依次给出询问的答案。
Sample Input
170337785
271451044
22430280
969056313
206452321
3
3 1 0 2
2 3 1 4
3 1 4 0
271451044
271451044
969056313
Sample Output
HINT
0:n,Q<=100
1,...,5:n<=2000
0,...,19:n<=20000,Q<=25000
题目大意:给出一列数,有q个询问,每次询问给出a,b,c,d, 意思是在[a,b]中找到一个左端点l,[c,d]中找到一个右端点r,使得[l,r]区间里的中位数最大,输出最大的中位数。题目强制在线。
对于每一次询问:我们二分答案,设当前答案为x,进行验证。我们把大于等于x的位置的贡献记为1,小于x的位置的贡献记为-1,那么只要找到一个[l,r]区间满足sum(l,r)的值>=0 (ps. 这里说的中位数感觉应该要>=1吧,因为题目里说两个数列都是0开始,除法为向下取整,并且是从大到小排序,手算一下,sum(l,r)的值应该要>0的时候才是能满足的),那么这个答案就是满足题意的,继续找是否有更大的答案,同理验证。
那么问题就转为如何找到这样的一个(l,r)区间:
我们用一个线段树来记录每个位置的贡献为1还是-1(大于等于x的贡献为1,小于的贡献为-1),那么只要看满足题意的max(sum(l,r))是否大于等于0就可以了。因为l在[a,b],r在[c,d], 那么[b,c]这个区间里的值肯定是必须要计算的,记为find_sum(b,c),然后我们找到[a,b-1]这个区间里向右对齐的最大和,记为max(find_r(a,b-1),0),[c+1,d]中向左对齐的最大和,记为max(find_l(c+1,d),0) 。(这里要取max是因为可以一个都不取,所以最小的值是0)最后判断max(sum(l,r))+max(find_r(a,b-1),0)+max(find_l(c+1,d),0)>=0是否成立。
但是这样的话需要n棵线段树(每个不同的值为基础,贡献是不同的),我们考虑简化,我们可以用可持久化线段树。先将n个数排序,再插入可持久化线段树中,因为前一个数和当前这棵树只有一个位置是不同的,所以可以用可持久化线段树来简化问题。
这样就结束了。
#include<cstdio>
#include<map>
#include<iostream>
#include<cstring>
#include<algorithm>
#define INF 1e9
#define ll long long
#define N 25005
#define M 20
using namespace std;
int tr[N*M],rt[N],L[N*M],R[N*M],sz,n,m,q[5],a[N],b[N],ls[N*M],rs[N*M],x,T;
map<int,int>mp;
void updata(int p){
tr[p]=tr[ls[p]]+tr[rs[p]];
L[p]=max(L[ls[p]],tr[ls[p]]+L[rs[p]]);
R[p]=max(R[rs[p]],tr[rs[p]]+R[ls[p]]);
}
void build(int &p,int l,int r){
if(!p) p=++sz;
if(l==r){
tr[p]=L[p]=R[p]=1;
return ;
}
int mid=(l+r)/2;
build(ls[p],l,mid);build(rs[p],mid+1,r);
updata(p);
}
void insert(int p1,int &p,int l,int r,int x){
if(!p) p=++sz;
if(l>x||r<x) return ;
if(l==r){
tr[p]=L[p]=R[p]=-1;
return ;
}
int mid=(l+r)/2;
if(x<=mid){rs[p]=rs[p1];insert(ls[p1],ls[p],l,mid,x);}
else {ls[p]=ls[p1];insert(rs[p1],rs[p],mid+1,r,x);}
updata(p);
}
int find_sum(int p,int l,int r,int x,int y){
if(l>y||x>r) return 0;
// if(tr[p]==0) return 0;
if(x<=l&&r<=y) return tr[p];
int mid=(l+r)/2;
return find_sum(ls[p],l,mid,x,y)+find_sum(rs[p],mid+1,r,x,y);
}
int find_l(int p,int l,int r,int x,int y){
if(l>y||x>r) return 0;
if(x<=l&&r<=y) return L[p];
int mid=(l+r)/2;
if(y<=mid) return find_l(ls[p],l,mid,x,y);
else if(x>mid) return find_l(rs[p],mid+1,r,x,y);
else return max(find_l(ls[p],l,mid,x,mid),find_sum(ls[p],l,mid,x,mid)+find_l(rs[p],mid+1,r,mid+1,y));
}
int find_r(int p,int l,int r,int x,int y){
if(l>y||x>r) return 0;
if(x<=l&&r<=y) return R[p];
int mid=(l+r)/2;
if(y<=mid) return find_r(ls[p],l,mid,x,y);
else if (x>mid) return find_r(rs[p],mid+1,r,x,y);
else return max(find_r(rs[p],mid+1,r,mid+1,y),find_sum(rs[p],mid+1,r,mid+1,y)+find_r(ls[p],l,mid,x,mid));
}
bool check(int x){
int l1=q[0],r1=q[1],l2=q[2],r2=q[3];
int tmp=find_sum(rt[x],1,n,r1,l2);
if(l2!=r2)tmp+=max(0,find_l(rt[x],1,n,l2+1,r2));
if(l1!=r1)tmp+=max(0,find_r(rt[x],1,n,l1,r1-1));
return tmp>=0;
}
int main(){
freopen("1.in","r",stdin);
freopen("1.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]),b[i]=a[i],mp[a[i]]=i;
sort(b+1,b+n+1);
//for(int i=1;i<=n;i++)
// pos[mp[b[i]]]=i;
build(rt[0],1,n);
for(int i=1;i<=n;i++)
insert(rt[i-1],rt[i],1,n,mp[b[i]]);
x=0;
scanf("%d",&T);
while(T--){
scanf("%d%d%d%d",&q[0],&q[1],&q[2],&q[3]);
for(int i=0;i<4;i++)
q[i]=(q[i]+x)%n;
for(int i=0;i<4;i++) q[i]++;
sort(q,q+4);
int l=1,r=n,mid;
while(l<r){
mid=(l+r+1)/2;
if(check(mid-1)) l=mid;
else r=mid-1;
}
printf("%d\n",b[l]);
x=b[l];
}
}