【BZOJ2653】Middle(主席树)

【BZOJ2653】Middle(主席树)

题面

BZOJ
洛谷

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

题解

陈老师神题啊,丽洁姐最神啦

还是二分答案
区间内的中位数大于一个值?
将大于答案的数赋值为\(1\),小于的赋值\(-1\)
既然是左端点在\([a,b]\),右端点在\([c,d]\)
那么也就是\([b+1,c-1]\)必须选
而现在要中位数尽可能大
所以要选的就是\([a,b]\)的最大右子段和
\([c,d]\)的最大左子段和

而答案一定是某个数列中的数
排序之后依次把自己位置上的\(1\)变成\(-1\)即可
不可能开这么多线段树
显然是在主席树上直接修改

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<queue>
using namespace std;
#define ll long long
#define RG register
#define MAX 22222
inline int read()
{
    RG int x=0,t=1;RG char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=-1,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return x*t;
}
struct Node
{
    int lv,rv,sum;
    int ls,rs,id;
}t[MAX<<6];
Node operator+(Node a,Node b)
{
    Node c;
    c.lv=max(a.lv,a.sum+b.lv);
    c.rv=max(b.rv,b.sum+a.rv);
    c.sum=a.sum+b.sum;
    c.ls=a.id;c.rs=b.id;
    return c;
}
int tot,rt[MAX];
void Build(int &x,int l,int r)
{
    x=++tot;t[x].id=x;
    if(l==r){t[x].lv=t[x].rv=t[x].sum=1;return;}
    int mid=(l+r)>>1;
    Build(t[x].ls,l,mid);Build(t[x].rs,mid+1,r);
    t[x]=t[t[x].ls]+t[t[x].rs];t[x].id=x;
}
void Modify(int &x,int ff,int l,int r,int p,int w)
{
    t[x=++tot]=t[ff];t[x].id=x;
    if(l==r){t[x].lv=t[x].rv=t[x].sum=t[x].sum+w;return;}
    int mid=(l+r)>>1;
    if(p<=mid)Modify(t[x].ls,t[ff].ls,l,mid,p,w);
    else Modify(t[x].rs,t[ff].rs,mid+1,r,p,w);
    t[x]=t[t[x].ls]+t[t[x].rs];t[x].id=x;
}
Node Query(int x,int l,int r,int L,int R)
{
    if(l==L&&r==R)return t[x];
    int mid=(l+r)>>1;
    if(R<=mid)return Query(t[x].ls,l,mid,L,R);
    if(L>mid)return Query(t[x].rs,mid+1,r,L,R);
    return Query(t[x].ls,l,mid,L,mid)+Query(t[x].rs,mid+1,r,mid+1,R);
}
int n,a[MAX],p[MAX];
bool cmp(int x,int y){return a[x]<a[y];}
int main()
{
    n=read();
    for(int i=1;i<=n;++i)a[i]=read();
    for(int i=1;i<=n;++i)p[i]=i;
    Build(rt[1],1,n);
    sort(&p[1],&p[n+1],cmp);
    for(int i=1;i<=n;++i)
    	Modify(rt[i+1],rt[i],1,n,p[i],-2);
    sort(&a[1],&a[n+1]);
    int Q=read();
    int ans=0,q[5];
    while(Q--)
    {
        for(int i=1;i<=4;++i)q[i]=(read()+ans)%n+1;
        sort(&q[1],&q[5]);
        int l=1,r=n,Ans=1;
        while(l<=r)
        {
            int mid=(l+r)>>1,ss=0;
            if(q[2]+1<=q[3]-1)ss+=Query(rt[mid],1,n,q[2]+1,q[3]-1).sum;
            ss+=Query(rt[mid],1,n,q[1],q[2]).rv;
            ss+=Query(rt[mid],1,n,q[3],q[4]).lv;
            if(ss>=0)Ans=mid,l=mid+1;
            else r=mid-1;
        }
        printf("%d\n",ans=a[Ans]);
    }
    return 0;
}

posted @ 2018-04-03 08:14  小蒟蒻yyb  阅读(458)  评论(0编辑  收藏  举报