【jzoj2902】【集训队互测 2012】【Middle】【可持久化线段树】

题目大意

一个长度为 n 的序列 a ,设其排过序之后为 b ,其中位数定义为 b[n/2] ,其中 a,b 从 0 开始标号 , 除法取下整。
给你一个长度为 n 的序列 s 。回答 Q 个这样的询问 : s 的左端点在 [a,b] 之间 , 右端点在 [c,d] 之间的子序列中 ,最大的中位数。
其中 a<b<c<d
位置也从 0 开始标号。
我会使用一些方式强制你在线。

解题思路

考虑求一个区间里的中位数,我们会二分答案,检查大于等于答案减小于答案的个数,如果大于等于0答案就有可能更大,越大答案就越大。那我们发现[b+1,c-1]一定要取,[a,b]取右数最大值,[c,d]取左数最大值,加起来就可以调整答案。
假设当前答案为x,那我们把大于等于x的位置都标为1,小于的位置标为-1就可以用线段树维护以上信息。我们发现不同的答案只有n个,我们对每一个x都维护一颗线段树即可,发现相邻两个x不同权值的位置有一个,那我们可以按x的大小顺序建线段树,利用之前的信息就可以快速求出当前线段树,用可持久化线段树即可。

code

#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LF double
#define LL long long
#define Min(a,b) ((a<b)?a:b)
#define Max(a,b) ((a>b)?a:b)
#define Fo(i,j,k) for(int i=j;i<=k;i++)
#define Fd(i,j,k) for(int i=j;i>=k;i--)
using namespace std;
int const Mxn=2*1e4,Mxnm=1e5;
int N,Pon,A[Mxn+9],B[Mxn+9],C[9],T[Mxn*20+9][3],Son[Mxn*20+9][2];
bool Cmp(int X,int Y){return A[X]<A[Y];}
void Update(int Now){
    T[Now][0]=T[Son[Now][0]][0]+T[Son[Now][1]][0];
    T[Now][1]=Max(T[Son[Now][0]][1],T[Son[Now][0]][0]+T[Son[Now][1]][1]);
    T[Now][2]=Max(T[Son[Now][1]][2],T[Son[Now][0]][2]+T[Son[Now][1]][0]);
}
void Build(int Now,int L,int R){
    if(L==R){
        T[Now][0]=T[Now][1]=T[Now][2]=1;
        return;
    }
    int Mid=(L+R)>>1;
    Son[Now][0]=++Pon;Son[Now][1]=++Pon;
    Build(Son[Now][0],L,Mid);Build(Son[Now][1],Mid+1,R);
    Update(Now);
}
void Oper(int Pre,int Now,int L,int R,int V){
    if(L==R){
        T[Now][0]=T[Now][1]=T[Now][2]=-1;
        return;
    }
    int Mid=(L+R)>>1;
    if(V<=Mid){
        Son[Now][1]=Son[Pre][1];
        Son[Now][0]=++Pon;
        Oper(Son[Pre][0],Son[Now][0],L,Mid,V);
    }else{
        Son[Now][0]=Son[Pre][0];
        Son[Now][1]=++Pon;
        Oper(Son[Pre][1],Son[Now][1],Mid+1,R,V);
    }
    Update(Now);
}
int Qury(int Now,int L,int R,int L1,int R1,int Op){
    int Mid=(L+R)>>1;
    if((L==L1)&&(R==R1))return T[Now][Op];
    else if(R1<=Mid)return Qury(Son[Now][0],L,Mid,L1,R1,Op);
    else if(Mid<L1)return Qury(Son[Now][1],Mid+1,R,L1,R1,Op);
    else if(!Op)return Qury(Son[Now][0],L,Mid,L1,Mid,Op)
                                +Qury(Son[Now][1],Mid+1,R,Mid+1,R1,Op);
    else if(Op==1){
        int X1=Qury(Son[Now][0],L,Mid,L1,Mid,Op),
            X2=Qury(Son[Now][0],L,Mid,L1,Mid,0)
            +Qury(Son[Now][1],Mid+1,R,Mid+1,R1,Op);
        return Max(X1,X2);
    }else{
        int X1=Qury(Son[Now][0],L,Mid,L1,Mid,Op)+
            Qury(Son[Now][1],Mid+1,R,Mid+1,R1,0),
            X2=Qury(Son[Now][1],Mid+1,R,Mid+1,R1,Op);
        return Max(X1,X2);
    }
}
int main(){
    freopen("d.in","r",stdin);
    freopen("d.out","w",stdout);
    scanf("%d",&N);Pon=N;
    Fo(i,1,N)scanf("%d",&A[i]),B[i]=i;
    sort(B+1,B+N+1,Cmp);
    Build(1,1,N);
    Fo(i,2,N)Oper(i-1,i,1,N,B[i-1]);
    int Q,Last=0;scanf("%d",&Q);
    Fo(cas,1,Q){
        Fo(i,1,4)scanf("%d",&C[i]),C[i]=(C[i]+Last)%N+1;
        sort(C+1,C+5);
        int L=1,R=N,Mid;
        while(L!=R){
            Mid=(L+R+1)>>1;
            if(Qury(Mid,1,N,C[1],C[2],2)+Qury(Mid,1,N,C[3],C[4],1)
                +((C[2]+1!=C[3])?Qury(Mid,1,N,C[2]+1,C[3]-1,0):0)>=0)L=Mid;
            else R=Mid-1;
        }
        printf("%d\n",Last=A[B[L]]);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值