[国家集训队]middle

[国家集训队]middle

Description

给你一个长度为n的序列s。 回答Q个这样的询问:s的左端点在[a,b]之间,右端点在[c,d]之间的子序列中,最大的中位数。 强制在线
 

Solution

 考虑二分答案,把问题转化成可行性问题,然后发现b+1 - c-1 是一定必选的,另外两边要想最大就是维护一个连续最大子段和

然后每次二分值改变,只会影响线段树上log个位置,于是考虑主席树

#include<bits/stdc++.h>

using namespace std;

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

const int MAXN = 20000 + 5;

int n;

struct NUM
{
    int val;
    int pos;
    friend bool operator < (NUM a1,NUM a2)
    {
        return a1.val < a2.val;
    }
}a[MAXN];

struct P_Tree
{
    int lc,rc;
    int lmax;
    int rmax;
    int sum;
}tree[MAXN * 18];
int cnt,root[MAXN];
int q[4];

inline void pushup(int rt)
{
    tree[rt].sum = tree[tree[rt].lc].sum + tree[tree[rt].rc].sum;
    tree[rt].lmax = max(tree[tree[rt].lc].lmax,tree[tree[rt].rc].lmax+tree[tree[rt].lc].sum);
    tree[rt].rmax = max(tree[tree[rt].rc].rmax,tree[tree[rt].lc].rmax+tree[tree[rt].rc].sum);
}

inline void build(int &rt,int l,int r)
{
    if(!rt) rt = ++cnt;
    if(l == r)
    {
        tree[rt].lmax = tree[rt].rmax = tree[rt].sum = 1;
        return;
    }
    int mid = (l + r) >> 1;
    build(tree[rt].lc,l,mid);
    build(tree[rt].rc,mid+1,r);
    pushup(rt);
}

inline void insert(int &rt,int rt1,int l,int r,int k)
{
    if(!rt) rt = ++cnt;
    tree[rt] = tree[rt1];
    if(l == r)
    {
        tree[rt].sum = -1;
        tree[rt].lmax = -1;
        tree[rt].rmax = -1;
        return;
    }
    int mid = (l + r) >> 1;
    if(mid >= k) tree[rt].lc = 0,insert(tree[rt].lc,tree[rt1].lc,l,mid,k);
    else tree[rt].rc = 0,insert(tree[rt].rc,tree[rt1].rc,mid+1,r,k);
    pushup(rt);
}

inline int query3(int rt,int l,int r,int L,int R)
{
    if(L<=l&&R>=r) return tree[rt].sum;
    if(L>r||l>R) return 0;
    int mid = (l + r) >> 1;
    return query3(tree[rt].lc,l,mid,L,R) + query3(tree[rt].rc,mid+1,r,L,R);
}

inline int query1(int rt,int l,int r,int L,int R)
{
    if(L<=l&&R>=r) return tree[rt].rmax;
    //if(L>r||l>R) return 0;
    int mid = (l + r) >> 1;
    if(R <= mid) return query1(tree[rt].lc,l,mid,L,R);
    else if(L >= mid + 1) return query1(tree[rt].rc,mid+1,r,L,R); 
    return max(query1(tree[rt].rc,mid+1,r,mid+1,R),query3(tree[rt].rc,mid+1,r,mid+1,R) + query1(tree[rt].lc,l,mid,L,mid));
}

inline int query2(int rt,int l,int r,int L,int R)
{
    if(L<=l&&R>=r) return tree[rt].lmax;
//    if(L>r||l>R) return 0;
    int mid = (l + r) >> 1;
    if(R <= mid) return query2(tree[rt].lc,l,mid,L,R);
    else if(L >= mid + 1) return query2(tree[rt].rc,mid+1,r,L,R); 
    return max(query2(tree[rt].lc,l,mid,L,mid),query3(tree[rt].lc,l,mid,L,mid) + query2(tree[rt].rc,mid+1,r,mid+1,R));
}

inline bool check(int mid)
{
    int res = 0;
    res = query1(root[mid],1,n,q[0],q[1]) + query2(root[mid],1,n,q[2],q[3]);
    if(q[2]-1 >= q[1] + 1) res += query3(root[mid],1,n,q[1]+1,q[2]-1);
    return res >= 0;
}

int main()
{
    n = read();
    for(int i=1;i<=n;i++) a[i].val = read(),a[i].pos = i;
    sort(a+1,a+n+1);
    build(root[1],1,n);
    for(int i=2;i<=n+1;i++) insert(root[i],root[i-1],1,n,a[i-1].pos);
    int Q = read(),lastans = 0;
    while(Q--)
    {
        int A = read(),B = read(),C = read(),D = read();
        q[0] = (A + lastans) % n + 1,q[1] = (B + lastans) % n + 1,q[2] = (C + lastans) % n + 1,q[3] = (D + lastans) % n + 1;
        sort(q,q+4);
        int l = 1,r = n + 1,ans = 0;
        while(l <= r)
        {
            int mid = (l + r) >> 1;
            if(check(mid)) l = mid + 1,ans = mid;
            else r = mid - 1;
         } 
         lastans = a[ans].val;
         printf("%d\n",lastans);
    }
}

 

posted @ 2020-11-13 21:29  wlzs1432  阅读(93)  评论(0编辑  收藏  举报