【bzoj2653】middle

Description

一个长度为n的序列a,设其排过序之后为b,其中位数定义为b[n/2],其中a,b从0开始标号,除法取下整。给你一个
长度为n的序列s。回答Q个这样的询问:s的左端点在[a,b]之间,右端点在[c,d]之间的子序列中,最大的中位数。
其中a< b < c < d。位置也从0开始标号。我会使用一些方式强制你在线。
Input

第一行序列长度n。接下来n行按顺序给出a中的数。
接下来一行Q。然后Q行每行a,b,c,d,我们令上个询问的答案是
x(如果这是第一个询问则x=0)。
令数组q={(a+x)%n,(b+x)%n,(c+x)%n,(d+x)%n}。
将q从小到大排序之后,令真正的
要询问的a=q[0],b=q[1],c=q[2],d=q[3]。  
输入保证满足条件。
第一行所谓“排过序”指的是从大到小排序!
Output

Q行依次给出询问的答案。

Sample Input

5

170337785

271451044

22430280

969056313

206452321

3

3 1 0 2

2 3 1 4

3 1 4 0

271451044

271451044

969056313

Sample Output

HINT

  0:n,Q<=100

1,…,5:n<=2000

0,…,19:n<=20000,Q<=25000

题解
二分答案+可持久化线段树
对于每个数值,建立一颗线段树,比它小的数(记为-1)比它大的数(记为+1),记录区间和,左/右区间最大字段和。
二分中位数,去可持久化线段树中判断是否有更大的中位数。

代码

#include<bits/stdc++.h>
#define mod 1000000007
#define inf 1000000005
#define N 20005
typedef long long ll;
using namespace std;
inline int read()
{
    int 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*10+ch-'0';ch=getchar();}
    return x*f;
}
struct node{int v,p;}a[N];
int tot,q[5],rt[N],ls[N*20],rs[N*20],sum[N*20],L[N*20],R[N*20],n,lastans; 
bool cmp(node a,node b){return a.v<b.v;}
void update(int k)
{
    sum[k]=sum[ls[k]]+sum[rs[k]];
    L[k]=max(L[ls[k]],sum[ls[k]]+L[rs[k]]);
    R[k]=max(R[rs[k]],sum[rs[k]]+R[ls[k]]);
}
void build(int &k,int l,int r)
{
    k=++tot;
    if (l==r){sum[k]=L[k]=R[k]=1;return;}
    int mid=(l+r)>>1;
    build(ls[k],l,mid);build(rs[k],mid+1,r);
    update(k);
}
void insert(int &k1,int k2,int l,int r,int v)
{
    k1=++tot;
    ls[k1]=ls[k2];rs[k1]=rs[k2];
    if (l==r){sum[k1]=L[k1]=R[k1]=-1;return;}
    int mid=(l+r)>>1;
    if (v<=mid) insert(ls[k1],ls[k2],l,mid,v);
    else insert(rs[k1],rs[k2],mid+1,r,v);
    update(k1);
}
int que1(int k,int l,int r,int x,int y)
{
    if (l==x&&r==y) return sum[k];
    int mid=(l+r)>>1;
    if (y<=mid) return que1(ls[k],l,mid,x,y);
    else if (x>mid) return que1(rs[k],mid+1,r,x,y);
    else return que1(ls[k],l,mid,x,mid)+que1(rs[k],mid+1,r,mid+1,y);
}
int que2(int k,int l,int r,int x,int y)
{
    if (l==x&&r==y) return R[k];
    int mid=(l+r)>>1;
    if (y<=mid) return que2(ls[k],l,mid,x,y);
    else if (x>mid) return que2(rs[k],mid+1,r,x,y);
    else return max(que2(rs[k],mid+1,r,mid+1,y),que1(rs[k],mid+1,r,mid+1,y)+que2(ls[k],l,mid,x,mid));
}
int que3(int k,int l,int r,int x,int y)
{
    if (l==x&&r==y) return L[k];
    int mid=(l+r)>>1;
    if (y<=mid) return que3(ls[k],l,mid,x,y);
    else if (x>mid) return que3(rs[k],mid+1,r,x,y);
    else return max(que3(ls[k],l,mid,x,mid),que1(ls[k],l,mid,x,mid)+que3(rs[k],mid+1,r,mid+1,y));
}
bool judge(int mid,int a,int b,int c,int d)
{
    int ans=0;
    if (c-b>1) ans+=que1(rt[mid],1,n,b+1,c-1);
    ans+=que2(rt[mid],1,n,a,b);
    ans+=que3(rt[mid],1,n,c,d);
    return ans>=0;
}
int main()
{
    n=read();
    for (int i=1;i<=n;i++) a[i].v=read(),a[i].p=i;
    sort(a+1,a+n+1,cmp);
    build(rt[1],1,n);
    for (int i=2;i<=n;i++) insert(rt[i],rt[i-1],1,n,a[i-1].p);
    int Case=read(),lastans=0;
    while (Case--)
    {
        q[0]=(read()+lastans)%n+1;
        q[1]=(read()+lastans)%n+1;
        q[2]=(read()+lastans)%n+1;
        q[3]=(read()+lastans)%n+1;
        sort(q,q+4);
        int l=1,r=n;
        while (l!=r)
        {
            int mid=(l+r+1)>>1;
            if (judge(mid,q[0],q[1],q[2],q[3])) l=mid;else r=mid-1;
        }
        lastans=a[l].v;
        printf("%d\n",lastans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值