BZOJ 2653: middle(主席树+二分答案)

传送门

解题思路

  首先可以想到一种暴力做法,就是询问时二分,然后大于等于这个值的设为1,否则设为-1,然后就和GSS1那样统计答案。但是发现这样时间空间复杂度都很爆炸,所以考虑预处理,可以用主席树来做了。以\(rt[i]\)为根线段树维护二分的答案为\(i\)的线段树,线段树下标是位置。处理的时候就排一遍序,然后每次\(mid+1\)的时候发现只有\(mid\)这个位置的值要变成\(-1\),所以可以直接处理。

代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>

using namespace std;
const int N=20005;
const int M=N*20;

template<class T> void rd(T &x){
    x=0;char ch=getchar();
    while(!isdigit(ch)) ch=getchar();
    while(isdigit(ch)) x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
}
inline int qmax(int x,int y){return x>y?x:y;}

int n,m,a[N],id[N],lx[M],rx[M],sum[M],ls[M],rs[M],tot,rt[N];
int q[10];

inline bool cmp(int x,int y){return a[x]<a[y];}

void build(int &x,int l,int r){
    x=++tot;int mid=(l+r)>>1;
    if(l==r) {sum[x]=lx[x]=rx[x]=1;return ;}
    build(ls[x],l,mid);build(rs[x],mid+1,r);
    sum[x]=sum[ls[x]]+sum[rs[x]];
    lx[x]=rx[x]=sum[x];
}

void update(int pre,int &x,int l,int r,int k){
    x=++tot;sum[x]=sum[pre];rx[x]=rx[pre];
    ls[x]=ls[pre];rs[x]=rs[pre];lx[x]=lx[pre];  
    if(l==r) {lx[x]=rx[x]=sum[x]=-1;return ;}
    int mid=(l+r)>>1;
    if(k<=mid) update(ls[pre],ls[x],l,mid,k);
    else update(rs[pre],rs[x],mid+1,r,k);
    sum[x]=sum[ls[x]]+sum[rs[x]];
    lx[x]=qmax(lx[ls[x]],sum[ls[x]]+lx[rs[x]]);
    rx[x]=qmax(rx[rs[x]],sum[rs[x]]+rx[ls[x]]);
}

int query_sum(int x,int l,int r,int L,int R){
    if(L>R) return 0;
    if(L<=l && r<=R) return sum[x];
    int mid=(l+r)>>1,ret=0;
    if(L<=mid) ret+=query_sum(ls[x],l,mid,L,R);
    if(mid<R) ret+=query_sum(rs[x],mid+1,r,L,R);
    return ret;
}

int query_lx(int x,int l,int r,int L,int R){
    if(L<=l && r<=R) return lx[x];
    int mid=(l+r)>>1;
    if(L>mid) return query_lx(rs[x],mid+1,r,L,R);
    else if(mid>=R) return query_lx(ls[x],l,mid,L,R);
    else return qmax(query_lx(ls[x],l,mid,L,R),query_sum(ls[x],l,mid,L,R)+query_lx(rs[x],mid+1,r,L,R));
}

int query_rx(int x,int l,int r,int L,int R){
    if(L<=l && r<=R) return rx[x];
    int mid=(l+r)>>1;
    if(L>mid) return query_rx(rs[x],mid+1,r,L,R);
    else if(mid>=R) return query_rx(ls[x],l,mid,L,R);
    else return qmax(query_rx(rs[x],mid+1,r,L,R),query_sum(rs[x],mid+1,r,L,R)+query_rx(ls[x],l,mid,L,R));
}

inline bool check(int lim){
    int ret=0;
    ret+=query_sum(rt[lim],1,n,q[2]+1,q[3]-1);
    ret+=query_lx(rt[lim],1,n,q[3],q[4]);
    ret+=query_rx(rt[lim],1,n,q[1],q[2]);
    return ret>=0?1:0;
}

int main(){
    rd(n);int lstans=0;
    for(int i=1;i<=n;i++) rd(a[i]),id[i]=i;
    sort(id+1,id+1+n,cmp);build(rt[1],1,n);sort(a+1,a+1+n);
    for(int i=2;i<=n+1;i++) update(rt[i-1],rt[i],1,n,id[i-1]);
    for(rd(m);m;m--){
        rd(q[1]);rd(q[2]);rd(q[3]);rd(q[4]);
        q[1]=(q[1]+lstans)%n+1;q[2]=(q[2]+lstans)%n+1;
        q[3]=(q[3]+lstans)%n+1;q[4]=(q[4]+lstans)%n+1;
        sort(q+1,q+1+4);int L=1,R=n,mid;
        while(L<=R){
            mid=(L+R)>>1;
            if(check(mid)) lstans=mid,L=mid+1;
            else R=mid-1;
        }
        printf("%d\n",a[lstans]);lstans=a[lstans];
    }
    return 0;
}

转载于:https://www.cnblogs.com/sdfzsyq/p/10253506.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值