Loading

题解「Luogu2839 [国家集训队]middle」

题面

给一个序列 \(s\) ,回答 \(Q\) 个这样的询问:\(s\) 的左端点在 \([a,b]\) 中,右端点在 \([c,d]\) 中的子区间的最大中位数。

题解

首先要知道中位数怎么求:

二分出一个 \(mid\) ,判断中位数 \(m\)\(mid\) 的大小关系。将询问区间 \([l,r]\) 内所有 \(s_i < mid\) 赋值为 \(-1\)\(s_i\geq mid\) 赋值为 \(1\) ,令 \(sum=\sum_{i=l}^rs_i\) 分类讨论:

  • \(sum \geq 0\) ,则 \(m \geq mid\)
  • \(sum<0\) ,则 \(m<mid\)

但是由于本题询问的区间不固定,上面的做法就需要改动。

题目中要求 \(m\) 最大,所以对于一个值 \(mid\) ,我们要让 \(sum\) 尽量大,而区间 \([b+1,c-1]\)\(sum\) 是一定的,所以等价于区间 \([a,b],[c,d]\)\(sum\) 尽量大,这相当于求区间 \([a,b]\) 和最大的后缀与区间 \([c,d]\) 和最大的前缀(这里的前缀后缀长度都不为 \(0\) )。

然后就有一个做法了:对于离散化后中位数可能取到的每一个值开一棵线段树,维护区间和 \(sum\) 、最大前缀和 \(lsum\) 、最大后缀和 \(rsum\) 。但是这样空间复杂度有 \(O(n^2 {\rm{log}}n)\) ,显然爆炸。

不难发现值 \(x\) 对应的线段树与值 \(x+1\) 对应的线段树差别不大,也就是说我们记录了很多重复信息。显然,由 \(x\)\(x+1\) ,只有原序列中值为 \(x\) 的数对应的值由 \(1\) 变为了 \(-1\) ,这里就可以用一个类似主席树的东西继承值 \(x\) 对应的线段树上的信息。


\(\text{vector}\) 记录每个值的出现位置,每次以上一次修改为模板进行修改。

\(\text{Code}:\)

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <vector>
#define maxn 20005
#define Rint register int
#define INF 0x3f3f3f3f
using namespace std;
typedef long long lxl;

template <typename T>
inline T read()
{
    T x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
    return x*f;
}

int n,m,Q;
int A[maxn],B[maxn];
std::vector<int> P[maxn];
int rt[maxn];

struct Segment_Tree
{
    struct node
    {
        int sum,lsum,rsum;
        node(int x=0){sum=lsum=rsum=x;}
        inline node operator + (const node &T)const
        {
            node res;
            res.sum=sum+T.sum;
            res.lsum=max(lsum,sum+T.lsum);
            res.rsum=max(T.rsum,T.sum+rsum);
            return res;
        }
    }tree[maxn<<5];
    int tot,ch[maxn<<5][2];
    inline int build(int l,int r,int d)
    {
        int p=++tot;
        if(l==r) {tree[p]=node(A[l]>=d?1:-1);return p;}
        int mid=(l+r)>>1;
        ch[p][0]=build(l,mid,d);
        ch[p][1]=build(mid+1,r,d);
        tree[p]=tree[ch[p][0]]+tree[ch[p][1]];
        return p;
    }
    inline int update(int l,int r,int pos,int tmp)
    {
        int p=++tot;
        tree[p]=tree[tmp];
        ch[p][0]=ch[tmp][0];ch[p][1]=ch[tmp][1];
        if(l==r) {tree[p]=node(-1);return p;}
        int mid=(l+r)>>1;
        if(pos<=mid)
            ch[p][0]=update(l,mid,pos,ch[tmp][0]);
        else
            ch[p][1]=update(mid+1,r,pos,ch[tmp][1]);
        tree[p]=tree[ch[p][0]]+tree[ch[p][1]];
        return p;
    }
    inline node query(int p,int l,int r,int L,int R)
    {
        if(L<=l&&r<=R) return tree[p];
        int mid=(l+r)>>1;
        if(R<=mid) return query(ch[p][0],l,mid,L,R);
        else if(L>mid) return query(ch[p][1],mid+1,r,L,R);
        else return query(ch[p][0],l,mid,L,R)+query(ch[p][1],mid+1,r,L,R);
    }
}st;

int a,b,c,d;

inline int check(int mid)
{
    int res=0;
    if(b+1<=c-1) res+=st.query(rt[mid],1,n,b+1,c-1).sum;
    res+=st.query(rt[mid],1,n,a,b).rsum;
    res+=st.query(rt[mid],1,n,c,d).lsum;
    return res;
}

int main()
{
    // freopen("P2839.in","r",stdin);
    n=read<int >();
    for(int i=1;i<=n;++i)
        A[i]=B[i]=read<int >();
    sort(B+1,B+n+1);
    m=unique(B+1,B+n+1)-B-1;
    for(int i=1;i<=n;++i)
    {
        A[i]=lower_bound(B+1,B+m+1,A[i])-B;
        P[A[i]].push_back(i);
    }
    rt[1]=st.build(1,n,1);
    for(int i=2;i<=m;++i)
    {
        rt[i]=rt[i-1];
        for(vector<int>::iterator it=P[i-1].begin();it!=P[i-1].end();++it)
            rt[i]=st.update(1,n,*it,rt[i]);
    }
    int x=0,q[4];
    Q=read<int >();
    while(Q--)
    {
        q[0]=(read<int >()+x)%n+1,q[1]=(read<int >()+x)%n+1;
		q[2]=(read<int >()+x)%n+1,q[3]=(read<int >()+x)%n+1;
		sort(q,q+4);
		a=q[0],b=q[1],c=q[2],d=q[3];
        int l=1,r=m,res=-1;
        while(l<=r)
        {
            int mid=(l+r)>>1;
            if(check(mid)>=0) res=mid,l=mid+1;
            else r=mid-1;
        }
        printf("%d\n",x=B[res]);
    }
    return 0;
}

posted @ 2020-08-25 15:34  GoPoux  阅读(107)  评论(1编辑  收藏  举报