『结题记录』P2839 [国家集训队] middle

二分答案+主席树

他们吃什么

\(Description\)

给一个长度为 \(n\) 的序列,求左端点在 \([a,b]\) 之间,右端点在 \([c,d]\) 之间的子区间中的最大中位数。

\(Solution\)

考虑二分答案。

设二分到的值为 \(x\) ,令大于 \(x\) 的数为 \(1\) ,否则为 \(-1\) 。若区间和大于等于 \(0\) ,说明答案可以再大,小于则说明要变小。

如何维护每个数的二分序列( \(-1\) , \(1\) 的序列)?可以发现,将序列排序后, \(a_i\) 的二分序列可以由 \(a_{i-1}\) 的二分序列进行部分修改(将大于 \(a_{i-1}\) 小于 \(a_i\) 的数部分修改为 \(-1\) )得出,就相当于多个历史版本,所以可以用主席树维护。

因为要求最大中位数,所以要使区间和尽可能大。对于一个左端点在 \([a,b]\) 之间,右端点在 \([c,d]\) 之间的区间。可知 \([b+1,c-1]\) 区间是一定要取的,剩下的 \([a,b]\)\([c,d]\) 两个区间分别取最大后缀和与最大前缀和即可。可以参考小白逛公园

\(Code\)

#include <iostream>
#include <algorithm>
#define int long long
const int M = 2e4+10;
int n,m,root[M],cnt;
int ans;
int qu[5];
struct node
{int num,id;}aa[M];
struct Tree
{
    int ls,rs,sum,lsum,rsum;
}t[M<<5];
inline void pushUp(int q)
{
    t[q].sum=t[t[q].ls].sum+t[t[q].rs].sum;
    t[q].lsum=std::max(t[t[q].ls].lsum,t[t[q].ls].sum+t[t[q].rs].lsum);
    t[q].rsum=std::max(t[t[q].rs].rsum,t[t[q].rs].sum+t[t[q].ls].rsum);
    return ;
}
void build(int q,int l,int r)
{
    if(l==r){
        t[q].lsum=t[q].rsum=t[q].sum=1;
        return ;
    }
    int mid=(l+r)>>1;
    build(t[q].ls=++cnt,l,mid);
    build(t[q].rs=++cnt,mid+1,r);
    pushUp(q);
    return ;
}
void change(int p,int q,int l,int r,int x){
    if(l==r){
        t[q].lsum=t[q].rsum=t[q].sum=-1;
        return ;
    }
    t[q].ls=t[p].ls,t[q].rs=t[p].rs;
    int mid = (l+r)>>1;
    if(mid >= x) change(t[p].ls,t[q].ls=++cnt,l,mid,x);
    else change(t[p].rs,t[q].rs=++cnt,mid+1,r,x);
    pushUp(q);
    return ;
}
Tree getSum(int q,int l,int r,int x,int y)
{
    if(l >= x&&r <= y)
        return t[q];
    int mid =(l+r)>>1;
    bool f1=0,f2=0;
    if(mid >= x) f1=1;
    if(mid < y) f2=1;
    if(f1==1&&f2==0) return getSum(t[q].ls,l,mid,x,y);
    else if(f1==0&&f2==1) return getSum(t[q].rs,mid+1,r,x,y);
    else{
        Tree lt=getSum(t[q].ls,l,mid,x,y),rt=getSum(t[q].rs,mid+1,r,x,y),nt;
        nt.sum=lt.sum+rt.sum;
        nt.lsum=std::max(lt.lsum,lt.sum+rt.lsum);
        nt.rsum=std::max(rt.rsum,rt.sum+lt.rsum);
        return nt;
    }
}
inline bool check(int x,int a,int b,int c,int d)
{
    int sum=0;
    if(b+1<=c-1) sum+=getSum(root[x],1,n,b+1,c-1).sum;
    sum+=getSum(root[x],1,n,a,b).rsum;
    sum+=getSum(root[x],1,n,c,d).lsum;
    if(sum<0) return 0;
    else return 1;
}
int dic(int a,int b,int c,int d)
{
    int l=1,r=n;
    while(l<r){
        int mid = (l+r)>>1;
        if(check(mid,a,b,c,d)) l = mid+1;
        else r=mid;
    }
    return l;
}
inline bool cmp(int a,int b){return a<b;}
inline bool cmp2(node a,node b){return a.num < b.num;}
inline int read(){
    int num=0,fl=1;char c=getchar();
    while(c<'0'||c>'9'){
        if(c=='-') fl=-1;
        c=getchar();
    }
    while(c >='0'&&c <='9'){
        num=(num<<3)+(num<<1)+(c^48);
        c=getchar();
    }
    return num*fl;
}
signed main(){
    n=read();
    for(int i = 1;i<=n;i++) aa[i].num=read(),aa[i].id=i;
    std::sort(aa+1,aa+n+1,cmp2);
    build(root[0]=++cnt,1,n);
    for(int i = 1;i <= n;i++) change(root[i-1],root[i]=++cnt,1,n,aa[i].id);
    m=read();
    while(m--){
        qu[1]=(read()+ans)%n+1,qu[2]=(read()+ans)%n+1,qu[3]=(read()+ans)%n+1,qu[4]=(read()+ans)%n+1;
        std::sort(qu+1,qu+5,cmp);
        ans=aa[dic(qu[1],qu[2],qu[3],qu[4])].num;
        printf("%lld\n",ans);
    }
    return 0;  
}
posted @ 2023-10-28 19:06  tkt  阅读(26)  评论(0编辑  收藏  举报