【集训队互测 2012】Middle

本文介绍了一种使用可持久化线段树解决特定区间中位数查找问题的方法。通过二分搜索结合线段树的数据结构,实现了对于动态区间内最大中位数的有效查询。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description

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

Solution

怎么做

一看到什么中位数,k小数,数据范围又不大,那么就可以二分出一个mid,找出比mid小的个数有多少个。
因为每个数对当前判断的贡献只有是与不是,去或不去,那么用1或-1规划一下就好了。比mid的小的设为-1,否则设为1,那么区间[l…r]中如果中位数为mid那么就是这些1和-1的和加起来≥0。
因为b到c必须要取,所以把区间拆解一下变成[a..b]中的最长后缀和+[a+1…b-1]的和+[c…d]中的最长前缀和。

用什么?

强制在线,那么就是可持久化结构。还支持询问,取max。
可持久化线段树。

Code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
const int maxn=20007;
int i,j,k,l,n,m,ans,data[maxn],u,v,r,mid,num,tt;
int root[maxn],q[5];
struct node{
    int lda,sum,l,r,rda;
}t[25000*30],op;
struct nod{
    int a,b;
}a[maxn];
bool cmp(nod x,nod y){
    return x.a<y.a;
}
int read(){
    int x=0,f=-1;
    char ch=getchar();
    while(ch<'0'||ch>'9'){
        if(ch='-')f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x;
}
void merge(int x){
    t[x].sum=t[t[x].l].sum+t[t[x].r].sum;
    t[x].lda=max(t[t[x].l].lda,t[t[x].l].sum+t[t[x].r].lda);
    t[x].rda=max(t[t[x].r].rda,t[t[x].r].sum+t[t[x].l].rda);
}
void build(int &x,int l,int r){
    if(!x)x=++num;
    if(l==r){
        t[x].lda=t[x].sum=t[x].rda=1;
    }
    else{
        int mid=(l+r)/2;
        build(t[x].l,l,mid);
        build(t[x].r,mid+1,r);
        merge(x);
    }
}
void change(int &x,int l,int r,int y){
    t[++num]=t[x];x=num;
    if(l==r){t[x].lda=t[x].sum=t[x].rda=-1;return;}
    int mid=(l+r)/2;
    if(y<=mid)change(t[x].l,l,mid,y);
    else change(t[x].r,mid+1,r,y);
    merge(x);
}
node bing(node x,node y){
    node z;
    z.sum=x.sum+y.sum;
    z.l=x.l;z.r=y.r;
    z.lda=max(x.lda,x.sum+y.lda);
    z.rda=max(y.rda,y.sum+x.rda);
    return z;
}
node find(int x,int l,int r,int y,int z){
    if(y>z)return op;
    if(l==y&&r==z){
        return t[x];
    }
    else{
        int mid=(l+r)/2;
        if(z<=mid)return find(t[x].l,l,mid,y,z);
        else if (y>mid)return find(t[x].r,mid+1,r,y,z);
        else{
            return bing(find(t[x].l,l,mid,y,mid),find(t[x].r,mid+1,r,mid+1,z));    
        }
    }
}
int pan(int x){
    return find(root[x],1,n,q[1],q[2]).rda+find(root[x],1,n,q[2]+1,q[3]-1).sum
    +find(root[x],1,n,q[3],q[4]).lda;    
}
int main(){
    n=read();
    fo(i,1,n){
        a[i].a=read();
        a[i].b=i;
    }
    sort(a+1,a+1+n,cmp);
    build(root[1],1,n);
    fo(i,2,n){
        root[i]=root[i-1];
        change(root[i],1,n,a[i-1].b);
    }
    m=read();
    fo(i,1,m){
        k=read();tt=read();u=read();v=read();
        q[1]=(k+ans)%n+1;q[2]=(tt+ans)%n+1;q[3]=(u+ans)%n+1;q[4]=(v+ans)%n+1;
        sort(q+1,q+5);
        l=1,r=n;
        while(l<r){
            mid=(l+r+1)/2;
            int pp=pan(mid);
            if(pp>=0)l=mid;else r=mid-1;
        }
        ans=a[l].a;
        printf("%d\n",ans);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值