2653: middle
Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 1536 Solved: 855
[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
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
Source
分析:
首先我们大概都能够想到二分答案然后判断...
怎么判断?一个很经典的思想,我才不会告诉你我没有想到QAQ...
对于一个序列S,设其中位数位M,二分答案位ans,如果M>=ans,那么一定满足S中有大于等于$\frac{|S|}{2}$的数字大于等于ans,所以我们需要在给顶序列中寻找合法的子序列使得存在M>=ans,我们可以把大于等于ans的数字置成1,小于ans的数字置为0,然后问题就转化为了在合法序列中寻找最大子段和使得其大于等于0...这就是基础的线段树问题,但是这样我们就需要n个线段树,很显然这是不兹磁的...所以我们先把ans=1(离散化之后)的线段树建出来,也就是说每个位置都是1,然后对于以后的i,用可持久化线段树单点修改压空间...查询的时候就在对应的线段树上查询即可...
代码:
不要问我为什么我要手写lower_bound还写得这么鬼畜...因为我开心...调都没调就A了...
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
//by NeighThorn
using namespace std;
const int maxn=20000+5,maxm=7000000+5;
int n,q,len,tot,ls[maxm],rs[maxm],sum[maxm],lmax[maxm],rmax[maxm],root[maxn];
long long a[maxn],mp[maxn];
vector<int> M[maxn];
inline int find(long long _){
int __=1,___=len,____;
while(__<=___){
int _____=(__+___)>>1;
if(mp[_____]>=_)
____=_____,___=_____-1;
else
__=_____+1;
}
return ____;
}
inline void build(int l,int r,int &x){
x=++tot;
if(l==r){
lmax[x]=rmax[x]=sum[x]=1;
return;
}
int mid=(l+r)>>1;
build(l,mid,ls[x]),build(mid+1,r,rs[x]);
sum[x]=sum[ls[x]]+sum[rs[x]];
lmax[x]=max(lmax[ls[x]],sum[ls[x]]+lmax[rs[x]]);
rmax[x]=max(rmax[rs[x]],sum[rs[x]]+rmax[ls[x]]);
}
inline void change(int l,int r,int x,int &y,int pos,int val){
y=++tot;
if(l==r){
lmax[y]=rmax[y]=sum[y]=val;
return;
}
int mid=(l+r)>>1;ls[y]=ls[x],rs[y]=rs[x];
if(pos<=mid)
change(l,mid,ls[x],ls[y],pos,val);
else
change(mid+1,r,rs[x],rs[y],pos,val);
sum[y]=sum[ls[y]]+sum[rs[y]];
lmax[y]=max(lmax[ls[y]],sum[ls[y]]+lmax[rs[y]]);
rmax[y]=max(rmax[rs[y]],sum[rs[y]]+rmax[ls[y]]);
}
inline int querysum(int l,int r,int L,int R,int x){
if(l==L&&r==R)
return sum[x];
int mid=(l+r)>>1;
if(R<=mid)
return querysum(l,mid,L,R,ls[x]);
else if(L>mid)
return querysum(mid+1,r,L,R,rs[x]);
else
return querysum(l,mid,L,mid,ls[x])+querysum(mid+1,r,mid+1,R,rs[x]);
}
inline int queryrmax(int l,int r,int L,int R,int x){
if(l==L&&r==R)
return rmax[x];
int mid=(l+r)>>1;
if(R<=mid)
return queryrmax(l,mid,L,R,ls[x]);
else if(L>mid)
return queryrmax(mid+1,r,L,R,rs[x]);
else
return max(queryrmax(l,mid,L,mid,ls[x])+querysum(mid+1,r,mid+1,R,rs[x]),queryrmax(mid+1,r,mid+1,R,rs[x]));
}
inline int querylmax(int l,int r,int L,int R,int x){
if(l==L&&r==R)
return lmax[x];
int mid=(l+r)>>1,ans;
if(R<=mid)
return querylmax(l,mid,L,R,ls[x]);
else if(L>mid)
return querylmax(mid+1,r,L,R,rs[x]);
else
return max(querylmax(mid+1,r,mid+1,R,rs[x])+querysum(l,mid,L,mid,ls[x]),querylmax(l,mid,L,mid,ls[x]));
}
signed main(void){
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%lld",&a[i]),mp[i]=a[i];
sort(mp+1,mp+n+1),len=unique(mp+1,mp+n+1)-mp-1;
for(int i=1;i<=n;i++)
a[i]=find(a[i]),M[a[i]].push_back(i);
build(1,n,root[1]);
for(int i=2;i<=len;i++)
if(M[i-1].size()>0){
change(1,n,root[i-1],root[i],M[i-1][0],-1);
for(int j=0;j<M[i-1].size();j++)
change(1,n,root[i],root[i],M[i-1][j],-1);
}
scanf("%d",&q);long long ans=0,a[4];
for(int i=1;i<=q;i++){
for(int j=0;j<4;j++)
scanf("%lld",&a[j]),a[j]=(a[j]+ans)%n,a[j]++;
sort(a,a+4);
int l=1,r=len;
while(l<=r){
int mid=(l+r)>>1;
int lm=queryrmax(1,n,a[0],a[1],root[mid]);
int s=0;
if(a[1]+1<=a[2]-1)
s=querysum(1,n,a[1]+1,a[2]-1,root[mid]);
int rm=querylmax(1,n,a[2],a[3],root[mid]);
if(lm+s+rm>=0)
ans=mid,l=mid+1;
else
r=mid-1;
}ans=mp[ans];
printf("%d\n",ans);
}
return 0;
}
By NeighThorn