BZOJ 2653 middle 二分+主席树

题意:
给定一个序列,多次询问左端点在[a,b],右端点在[c,d]的区间的中位数的最大值。
解析:
乍一看题,这什么玩意。
冷静冷静。
寻求中位数最大值?
上二分可不可以?
有没有单调性?
。。。。
我们发现,居然tmd有单调性?
我也是醉了
我们可以二分出来当前某个中位数。
然后我们把序列里所有的比它小的数看成-1,所有的比它大的数看成1。
然后我们只需要找左端点在[a,b],右端点在[c,d]是否存在一个区间使得该区间的和大于等于0
如果有的话,那么显然答案是可以再递增的,反之就需要减小答案。
然后我们就可以采取这种方式来二分中位数了。
但是值得注意的是,我们不能每一次对序列中的所有元素重新赋值,那样的话根本遭不住。
这时候可持久化数据结构的优势就体现出来了。
我们可以用主席树来解决这个问题。
具体方案就是我们把序列中的元素排序,从小到大加入。
初始把每个元素的值看作是1
然后每一次加入就相当于把上一个的值变成-1.(相等也可以改上一个不会影响答案)
这样我们每一次加入都是在原来的历史版本上重新搞一个新版本,主席树即可搞定。
至于二分部分。
左端点在[a,b],右端点在[c,d]中的某一段最大和区间。
显然(b,c)是必须取的。
于是就是rmax([a,b])+sum((b,c))+lmax([c,d])。
线段树里维护一个从左起的最大连续和以及从右起的最大连续和以及整段和即可。
注意上方()与[]的区别。
代码:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 20010
#define Q 25010
#define M 401000
#define INF 0x3f3f3f3f
using namespace std;
int n,NO,q;
int root[N];
int size; 
struct node
{
    int lson,rson,sum,lmax,rmax;
}seg[M];
struct element
{
    int val,no;
    friend istream& operator >> (istream &_,element &a)
    {scanf("%d",&a.val),a.no=++NO;return _;}
}a[N];
int cmp(element a,element b)
{
    return a.val<b.val;
}
void pushup(int rt)
{
    int l=seg[rt].lson,r=seg[rt].rson;
    seg[rt].sum=seg[l].sum+seg[r].sum;
    seg[rt].lmax=max(seg[l].lmax,seg[l].sum+seg[r].lmax);
    seg[rt].rmax=max(seg[r].rmax,seg[r].sum+seg[l].rmax);
}
void build(int &rt,int l,int r)
{
    if(!rt)
        rt=++size;
    if(l==r)
    {
        seg[rt].lmax=seg[rt].rmax=seg[rt].sum=1;
        return;
    }
    int mid=(l+r)>>1;
    build(seg[rt].lson,l,mid);
    build(seg[rt].rson,mid+1,r);
    pushup(rt);
}
void update(int y,int &x,int l,int r,int v,int val)
{
    x=++size;
    seg[x]=seg[y];
    if(l==r)
    {
        seg[x].lmax=seg[x].rmax=seg[x].sum=val;
        return;
    }
    int mid=(l+r)>>1;
    if(v<=mid)update(seg[y].lson,seg[x].lson,l,mid,v,val);
    else update(seg[y].rson,seg[x].rson,mid+1,r,v,val);
    pushup(x);
}
int query_sum(int rt,int L,int R,int l,int r)
{
    if(L>R)return 0;
    int ret=0;
    if(L<=l&&r<=R)
    {
        return seg[rt].sum;
    }
    int mid=(l+r)>>1;
    if(L<=mid)ret+=query_sum(seg[rt].lson,L,R,l,mid);
    if(R>mid)ret+=query_sum(seg[rt].rson,L,R,mid+1,r);
    return ret;
}
int query_rmax(int rt,int L,int R,int l,int r)
{
    if(L>R)return 0;
    int ret=-INF;
    if(L<=l&&r<=R)
    {
        return seg[rt].rmax;
    }
    int mid=(l+r)>>1;
    if(R<=mid)ret=max(ret,query_rmax(seg[rt].lson,L,R,l,mid));
    else if(L>mid)ret=max(ret,query_rmax(seg[rt].rson,L,R,mid+1,r));
    else
    {
        ret=max(ret,query_rmax(seg[rt].lson,L,mid,l,mid)+query_sum(seg[rt].rson,mid+1,R,mid+1,r));
        ret=max(ret,query_rmax(seg[rt].rson,mid+1,R,mid+1,r));
    }
    return ret;
}
int query_lmax(int rt,int L,int R,int l,int r)
{
    if(L>R)return 0;
    int ret=-INF;
    if(L<=l&&r<=R)
    {
        return seg[rt].lmax;
    }
    int mid=(l+r)>>1;
    if(R<=mid)ret=max(ret,query_lmax(seg[rt].lson,L,R,l,mid));
    else if(L>mid)ret=max(ret,query_lmax(seg[rt].rson,L,R,mid+1,r));
    else
    {
        ret=max(ret,query_sum(seg[rt].lson,L,mid,l,mid)+query_lmax(seg[rt].rson,mid+1,R,mid+1,r));
        ret=max(ret,query_lmax(seg[rt].lson,L,mid,l,mid));
    }
    return ret;
}
int query[5];
bool check(int x)
{
    return query_rmax(root[x],query[1],query[2],1,n)+query_sum(root[x],query[2]+1,query[3]-1,1,n)+query_lmax(root[x],query[3],query[4],1,n)>=0;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)cin>>a[i];
    sort(a+1,a+n+1,cmp);
    build(root[1],1,n);
    for(int i=2;i<=n;i++)
        update(root[i-1],root[i],1,n,a[i-1].no,-1);
    int ans=0;
    scanf("%d",&q);
    for(int i=1;i<=q;i++)
    {
        scanf("%d%d%d%d",&query[1],&query[2],&query[3],&query[4]);
        for(int i=1;i<=4;i++)query[i]=(query[i]+ans)%n+1;
        sort(query+1,query+5);
        int tmp=0,l=1,r=n;
        while(l<=r)
        {
            int mid=(l+r)>>1;
            if(check(mid))tmp=mid,l=mid+1;
            else r=mid-1;
        }
        ans=a[tmp].val;
        printf("%d\n",ans);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值