[bzoj2653]middle

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);
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值