[国家集训队]Middle

Description:

一个长度为n的序列a,设其排过序之后为b,其中位数定义为b[n/2],其中a,b从0开始标号,除法取下整。

给你一个长度为n的序列s。

回答Q个这样的询问:s的左端点在[a,b]之间,右端点在[c,d]之间的子序列中,最大的中位数。

其中a<b<c<d。

位置也从0开始标号。

我会使用一些方式强制你在线。

Hint:

\(n,q \le 5*10^4\)

Solution:

正解方法很简单,但真的很难想

区间不确定,无法直接找中位数,考虑二分答案

每次把大于等于该数的位置赋为1,其余赋为-1

然后加上[b+1,c-1]的和,[c,d]的最大前缀和以及[a,b]的最大后缀和(这样一定能保证选到限制区间中的最优区间)

看是否大于等于0就行

由于二分的单调性,我们只需从小到大依次把每个数从-1修改为1,考虑主席树

#include <map>
#include <set>
#include <stack>
#include <cmath>
#include <queue>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const int mxn=1e6+5;
int n,m,s,cnt,tot,q[6],hd[mxn];
int a[mxn],b[mxn],rk[mxn],rt[mxn<<6],ls[mxn<<6],rs[mxn<<6],lmx[mxn<<6],rmx[mxn<<6],sum[mxn<<6];

inline int read() {
    char c=getchar(); int x=0,f=1;
    while(c>'9'||c<'0') {if(c=='-') f=-1;c=getchar();}
    while(c<='9'&&c>='0') {x=(x<<3)+(x<<1)+(c&15);c=getchar();}
    return x*f;
}
inline void chkmax(int &x,int y) {if(x<y) x=y;}
inline void chkmin(int &x,int y) {if(x>y) x=y;}

struct ed {
    int to,nxt;
}t[mxn<<1];

inline void add(int u,int v) {
    t[++cnt]=(ed) {v,hd[u]}; hd[u]=cnt;
}

int cmp(int x,int y) {
    return a[x]<a[y];
}

void build(int l,int r,int &p) {
    if(!p) p=++tot;
    if(l==r) {
        sum[p]=lmx[p]=rmx[p]=1; 
        return ;
    }
    int mid=(l+r)>>1;
    build(l,mid,ls[p]); build(mid+1,r,rs[p]);
    sum[p]=sum[ls[p]]+sum[rs[p]];
    lmx[p]=max(lmx[ls[p]],sum[ls[p]]+lmx[rs[p]]);
    rmx[p]=max(rmx[rs[p]],sum[rs[p]]+rmx[ls[p]]);
}

void update(int las,int &p,int l,int r,int pos) {
    if(!p) p=++tot;
    if(l==r) {
        sum[p]=lmx[p]=rmx[p]=-1;
        return ;
    }
    int mid=(l+r)>>1;
    if(pos<=mid) update(ls[las],ls[p],l,mid,pos),rs[p]=rs[las];
    else update(rs[las],rs[p],mid+1,r,pos),ls[p]=ls[las];
    sum[p]=sum[ls[p]]+sum[rs[p]];
    lmx[p]=max(lmx[ls[p]],sum[ls[p]]+lmx[rs[p]]);
    rmx[p]=max(rmx[rs[p]],sum[rs[p]]+rmx[ls[p]]);
}

int query(int l,int r,int ql,int qr,int p) {
    if(ql<=l&&r<=qr) return sum[p];
    int mid=(l+r)>>1; int res=0;
    if(ql<=mid) res+=query(l,mid,ql,qr,ls[p]);
    if(qr>mid) res+=query(mid+1,r,ql,qr,rs[p]);
    return res;
}

int querylmx(int l,int r,int ql,int qr,int p) {
    if(ql<=l&&r<=qr) return lmx[p];
    int mid=(l+r)>>1; 
    if(qr<=mid) return querylmx(l,mid,ql,qr,ls[p]);
    else if(ql>mid) return querylmx(mid+1,r,ql,qr,rs[p]);
    else return max(querylmx(l,mid,ql,qr,ls[p]),query(l,mid,ql,qr,ls[p])+querylmx(mid+1,r,ql,qr,rs[p]));
}

int queryrmx(int l,int r,int ql,int qr,int p) {
    if(ql<=l&&r<=qr) return rmx[p];
    int mid=(l+r)>>1;
    if(qr<=mid) return queryrmx(l,mid,ql,qr,ls[p]);
    else if(ql>mid) return queryrmx(mid+1,r,ql,qr,rs[p]);
    else return max(queryrmx(mid+1,r,ql,qr,rs[p]),query(mid+1,r,ql,qr,rs[p])+queryrmx(l,mid,ql,qr,ls[p]));
}

int check(int id,int x,int y,int w,int z) {
    int res=0;
    if(y+1<=w-1) res+=query(1,n,y+1,w-1,rt[id]);
    res+=querylmx(1,n,w,z,rt[id]); res+=queryrmx(1,n,x,y,rt[id]);
    return res>=0;
}

int main()
{
    n=read(); int ans=0;
    for(int i=1;i<=n;++i) a[i]=b[i]=read(),rk[i]=i;
    sort(b+1,b+n+1);
    for(int i=1;i<=n;++i) a[i]=lower_bound(b+1,b+n+1,a[i])-b;
    sort(rk+1,rk+n+1,cmp); build(1,n,rt[1]);
    for(int i=2;i<=n;++i) update(rt[i-1],rt[i],1,n,rk[i-1]);
    m=read(); 
    for(int i=1;i<=m;++i) {
        for(int j=1;j<=4;++j) q[j]=(read()+ans)%n; 
        sort(q+1,q+5);
        int l=1,r=n;
        while(l<r) {
            int mid=(l+r+1)>>1;
            if(check(mid,q[1]+1,q[2]+1,q[3]+1,q[4]+1)) l=mid;
            else r=mid-1;
        } 
        printf("%d\n",ans=b[l]);
    }
    return 0;
}

posted @ 2019-03-22 16:58  cloud_9  阅读(211)  评论(0编辑  收藏  举报