[国家集训队]middle 题解

题面传送门

这道题我们首先会想到处理中位数的常见方法:

二分枚举答案ans,将大于ans的数变为1,小于的则变成-1;如果这个区间的和大于1,则说明中位数比枚举的答案要大,否则要小;

但是这道题的区间并不确定,难道我们要n*n枚举?肯定不行;

观察性质,我们发现,对于区间[b,c],我们肯定要选择,那么先把这个区间的和加入到答案里;

因为题中说选择尽量大的中位数,根据二分时的性质,区间和越大,中位数越大;所以我们所选择的区间的和要最大;

也就是说,我们还要选取rmax[a,b]和lmax[c,d];

综上所述,对于每次二分的判定,如果rmax[a,b]+lmax[c,d]+sum(b,c)>=0,则说明中位数比现在枚举的答案大,然后更改二分条件L=mid+1;

我们可以对于每次二分的答案将区间改成01序列,利用线段树可以做到nlogn;

可是这样空间复杂度原地爆炸,于是想到了我们的好朋友:主席树;

发现:如果二分的答案ans变为ans+1,那么ans+1所对应的线段树上相对于ans的线段树只有等于ans+1的点的权值有所变动,显然每次多需要建的树节点均摊下来只有(logn);

于是开心的用$n\log ^{2}n$的时间复杂度AC掉了这道题;

#include <bits/stdc++.h>
#define inc(i,a,b) for(register int i=a;i<=b;i++)
using namespace std;
int aa[20010],lisan;
int in[10],root[20010];
class node2{
    public:
    int x,id;
}query[200010];
bool cmp(node2 x,node2 y){
    return x.x<y.x;
}
class node{
    public:
    int lson,rson;
    int sum,lmax,rmax;
    #define mid (l+r)/2
}tree[2000010];
int tot;
void updata(int k){
    tree[k].sum=tree[tree[k].lson].sum+tree[tree[k].rson].sum;
    tree[k].lmax=max(tree[tree[k].lson].lmax,tree[tree[k].rson].lmax+tree[tree[k].lson].sum);
    tree[k].rmax=max(tree[tree[k].rson].rmax,tree[tree[k].lson].rmax+tree[tree[k].rson].sum);
}
int build(int l,int r){
    int root=++tot;
    if(l==r){
        tree[root].sum=tree[root].lmax=tree[root].rmax=1;
        return root;
    }
    tree[root].lson=build(l,mid);
    tree[root].rson=build(mid+1,r);
    updata(root); return root;
}
void add(int &now,int pre,int l,int r,int goal,int value){
    now=++tot;
    tree[now]=tree[pre];
    if(l==r){
        tree[now].lmax=tree[now].rmax=tree[now].sum=value;
        return;
    }
    if(goal<=mid){
        add(tree[now].lson,tree[pre].lson,l,mid,goal,value);
    }
    else{
        add(tree[now].rson,tree[pre].rson,mid+1,r,goal,value);
    }
    updata(now);
}
int querysum(int now,int l,int r,int x,int y){
    if(x>r||y<l) return 0;
    if(x<=l&&r<=y) return tree[now].sum;
    return (querysum(tree[now].lson,l,mid,x,y)+querysum(tree[now].rson,mid+1,r,x,y));
}
int querylmax(int now,int l,int r,int x,int y){
    if(x>r||y<l) return -99999999;
    if(x<=l&&r<=y) return tree[now].lmax;
    return max(querylmax(tree[now].lson,l,mid,x,y),querylmax(tree[now].rson,mid+1,r,x,y)+querysum(tree[now].lson,l,mid,x,y));
}
int queryrmax(int now,int l,int r,int x,int y){
    if(x>r||y<l) return -99999999;
    if(x<=l&&r<=y) return tree[now].rmax;
    int tmp1=queryrmax(tree[now].rson,mid+1,r,x,y),tmp2=queryrmax(tree[now].lson,l,mid,x,y)+querysum(tree[now].rson,mid+1,r,x,y);
    return max(tmp1,tmp2);
}
int n; 
bool check(int now,int a,int b,int c,int d){
    int sum=0;
    if(b+1<=c-1) sum+=querysum(root[now],1,n,b+1,c-1);
    sum+=queryrmax(root[now],1,n,a,b);
    sum+=querylmax(root[now],1,n,c,d);
    if(sum>=0) return 1;
    else return 0;
}
int main()
{
    scanf("%d",&n);
    inc(i,1,n){
        scanf("%d",&query[i].x);
        query[i].id=i;
    }
    sort(query+1,query+1+n,cmp);
    root[1]=build(1,n);
    inc(i,2,n+1){
        add(root[i],root[i-1],1,n,query[i-1].id,-1);
    }
    int q; scanf("%d",&q);
    int ans=0;
    inc(i,1,q){
        scanf("%d%d%d%d",&in[0],&in[1],&in[2],&in[3]);
        in[0]=(in[0]+ans)%n; in[1]=(in[1]+ans)%n; in[2]=(in[2]+ans)%n; in[3]=(in[3]+ans)%n;
        sort(in,in+4);
        int L=1,R=n;
        while(L<=R){
            int midd=(L+R)/2;
            if(check(midd,in[0]+1,in[1]+1,in[2]+1,in[3]+1)){
                L=midd+1; ans=midd;
            }
            else{
                R=midd-1;
            }
        }
        ans=query[ans].x;
        printf("%d\n",ans);
    }
}
/*
5
170337785
271451044
22430280
969056313
206452321
3
3 1 0 2
2 3 1 4
3 1 4 0
*/
posted @ 2019-12-27 17:39  神之右大臣  阅读(230)  评论(0编辑  收藏  举报