【集训队互测 2012】Middle

Description

给出一个序列A,长度为n。给出m次询问,每次询问a,b,c,d(a< b< c< d),求a<=l<=b,c<=r<=d的子序列中,最大的中位数是多少。(这里说的中位数就是中间的那个数)强制在线。
n<=20000,m<=25000

Solution

不知道有什么数据结构能维护中位数。
考虑二分答案,答案x合法就是存在一个序列,使得大于等于x的数的个数大于小于x的数的个数。
那我们可以吧每一个大于等于x的数看做1,小于x的数看做-1。那么一个序列合法就是它的和>=0。
那我们就相当于求一个左端点在[a~b],右端点在[c~d]的最大子段和。
如果这东西>=0,那么答案合法。
这个东西可以拆分成[a~b]的最大后缀和,[b+1~c-1]的和,和[c~d]最大前缀和。
线段树维护。
那么怎么在时限内建树呢?
发现每一个相邻的x的变化只有一条链,于是可以打可持久化线段树。(动态开节点)

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define N 20005
using namespace std;
struct note{int v,w;}a[N];
struct node{int s,lv,rv,l,r;}t[N*30];
bool cmp(note x,note y) {return x.v<y.v;}
int ans,tot,n,m,x,root[N],q[4];
void back(int v) {
    int x=t[v].l,y=t[v].r;
    t[v].s=t[x].s+t[y].s;
    t[v].lv=max(t[x].lv,t[x].s+t[y].lv);
    t[v].rv=max(t[y].rv,t[y].s+t[x].rv);
}
node merge(node y,node z) {
    node x;
    x.s=y.s+z.s;
    x.lv=max(y.lv,y.s+z.lv);
    x.rv=max(z.rv,z.s+y.rv);
    return x;
}
void build(int &v,int l,int r) {
    v=++tot;
    if (l==r) {t[v].s=t[v].lv=t[v].rv=1;return;}
    int m=(l+r)/2;
    build(t[v].l,l,m);build(t[v].r,m+1,r);
    back(v);
}
void change(int &v,int l,int r,int x) {
    t[++tot]=t[v];v=tot;
    if (l==r) {t[v].s=t[v].lv=t[v].rv=-1;return;}
    int m=(l+r)/2;
    if (x<=m) change(t[v].l,l,m,x);
    else change(t[v].r,m+1,r,x);
    back(v);
}
node find(int v,int l,int r,int x,int y) {
    if (x>y) return t[0];
    if (l==x&&r==y) return t[v];
    int m=(l+r)/2;
    if (y<=m) return find(t[v].l,l,m,x,y);
    else if (x>m) return find(t[v].r,m+1,r,x,y);
    else return merge(find(t[v].l,l,m,x,m),find(t[v].r,m+1,r,m+1,y));
}
bool check(int x) {
    return find(root[x],1,n,q[0],q[1]).rv+find(root[x],1,n,q[1]+1,q[2]-1).s+
    find(root[x],1,n,q[2],q[3]).lv>=0;
}
int main() {
    scanf("%d",&n);
    fo(i,1,n) scanf("%d",&a[i].v),a[i].w=i;
    sort(a+1,a+n+1,cmp);
    build(root[1],1,n);
    fo(i,2,n) root[i]=root[i-1],change(root[i],1,n,a[i-1].w);
    for(scanf("%d",&m);m;m--) {
        fo(i,0,3) scanf("%d",&x),q[i]=(ans+x)%n+1;sort(q,q+4);
        int l=1,r=n+1,mid;
        while (l<r) {
            mid=(l+r)/2;
            if (check(mid)) l=mid+1;else r=mid;
        }
        printf("%d\n",ans=a[l-1].v);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值