1. 程式人生 > 實用技巧 >題解 P2839 【[國家集訓隊]middle】

題解 P2839 【[國家集訓隊]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;
}