主席树练习6——P2839 [国家集训队]middle

蒟蒻的垂死挣扎

这题写得。。。。不想说什么了。

首先,对于每次询问,我们可以知道,范围内比中位数小的数是要等于比他大的数的,而这题有重复就意味着小的数与大的数之差要在这个中位数个数的范围内,为了取到最大,我们自然是要它取到极致。我们考虑用二分答案来取到这个值,每次如果大于等于它的数 要大于等于 比他小的数,则这个答案是满足的,就可以继续缩范围,这样我们就得到了一个不错的思路。

那么我们如何快速解决check呢?我们注意到,虽然它所给的左右端点都不确定,在一个区间内,但是我们可以贪心地想一想,我们要二分出最优解,当然就要放过更多的二分值,所以就相当于我们要求[a,b]区间后缀的 比二分值大的数个数-比二分值小的数个数 的最大值,[c,d]区间前缀的同样值,再加上[b+1,c-1]区间的所有数,最后再判定该值是否可行。

而最最重要的就是这些值怎么求了。这里终于要用到最最最最最重要的主席树啦,我们对于每一个值开一棵线段数,比他小的数赋为-1,比他大的赋为1,就转化为了求最大前缀、后缀子段和的问题,轻松愉快。而对于赋值的问题,从小到大加数,初始全为1,每次转移就将上一个值赋为-1,这样就能完美操作。

这里注意插点顺序(wa了好多遍)

// luogu-judger-enable-o2
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<map>
#include<queue>
#include<set>
#define RG register
#define N 100100
#define ls tr[x].le
#define rs tr[x].ri
#define ll long long
#define ld long double
using namespace std;

inline ll read(){
  RG ll x=0,o=1; RG char ch=getchar();
  while((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
  if(ch=='-') o=-1,ch=getchar();
  while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
  return x*o;
}

int len,n,m,a[N],b[N],yyb[N],tot,top,root[N],id[N];
struct mona { int le,ri; int fr,la,sum; } tr[N<<6];

//位置从0开始标号!!!

inline void Pushup(RG int x){
    tr[x].fr=max(tr[ls].fr,tr[ls].sum+tr[rs].fr);
    tr[x].la=max(tr[rs].la,tr[rs].sum+tr[ls].la);
    tr[x].sum=tr[ls].sum+tr[rs].sum;
}

inline void Build(RG int l,RG int r,RG int &x){
    RG int num=r-l+1; x=++tot,tr[x]=(mona) { 0,0,num,num,num };
    if(l==r) return ;
    RG int mid=(l+r)>>1;
    Build(l,mid,ls),Build(mid+1,r,rs);
}

inline void Modify(RG int l,RG int r,RG int &x,RG int w){
    tr[++tot]=tr[x],x=tot;
    if(l==r) { tr[x].fr=tr[x].la=tr[x].sum=-1; return ; }
    RG int mid=(l+r)>>1;
    if(w<=mid) Modify(l,mid,ls,w);
    else Modify(mid+1,r,rs,w);
    Pushup(x);
}

inline int Query_sum(RG int l,RG int r,RG int x,RG int L,RG int R){
    if(R<L) return 0;
    if(l==L&&r==R) return tr[x].sum;
    RG int mid=(l+r)>>1;
    if(R<=mid) return Query_sum(l,mid,ls,L,R);
    else if(L>mid) return Query_sum(mid+1,r,rs,L,R);
    else return (Query_sum(l,mid,ls,L,mid)+Query_sum(mid+1,r,rs,mid+1,R));
}

inline int Query_Fr(RG int l,RG int r,RG int x,RG int L,RG int R){
    if(l==L&&r==R) return tr[x].fr;
    RG int mid=(l+r)>>1;
    if(R<=mid) return Query_Fr(l,mid,ls,L,R);
    else if(L>mid) return Query_Fr(mid+1,r,rs,L,R);
    else{
        RG int Le=Query_Fr(l,mid,ls,L,mid),Ri=Query_Fr(mid+1,r,rs,mid+1,R);
        RG int ss=Query_sum(l,mid,ls,L,mid);
        return max(Le,ss+Ri);
    }
}

inline int Query_La(RG int l,RG int r,RG int x,RG int L,RG int R){
    if(l==L&&r==R) return tr[x].la;
    RG int mid=(l+r)>>1;
    if(R<=mid) return Query_La(l,mid,ls,L,R);
    else if(L>mid) return Query_La(mid+1,r,rs,L,R);
    else{
        RG int Le=Query_La(l,mid,ls,L,mid),Ri=Query_La(mid+1,r,rs,mid+1,R);
        RG int ss=Query_sum(mid+1,r,rs,mid+1,R);
        return max(Ri,ss+Le);
    }
}

bool cmp(RG int x,RG int y) { return a[x]<a[y]; }
int main(){
    n=read(),Build(1,n,root[1]);
    for(RG int i=1;i<=n;++i) a[i]=read(),id[i]=i;
    m=read(),sort(id+1,id+1+n,cmp);
    for(RG int i=2;i<=n;++i)
        root[i]=root[i-1],Modify(1,n,root[i],id[i-1]);
    
    for(RG int i=1,ans=0;i<=m;++i){
        RG int Q[5];
        for(RG int j=0;j<4;++j) Q[j]=(read()+ans)%n+1;
        sort(Q,Q+4); RG int le=1,ri=n;
//		for(RG int j=0;j<4;++j) cout<<Q[j]<<' ';cout<<endl;
        while(le<=ri){
            RG int mid=(le+ri)>>1;
            RG int sum=Query_sum(1,n,root[mid],Q[1]+1,Q[2]-1);
            RG int la=Query_La(1,n,root[mid],Q[0],Q[1]),fr=Query_Fr(1,n,root[mid],Q[2],Q[3]);
            if(la+fr+sum>=0) ans=a[id[mid]],le=mid+1;
            else ri=mid-1; //cout<<mid<<' '<<sum<<' '<<la<<' '<<fr<<endl;
        } printf("%d\n",ans);
    } return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值