BZOJ2653 middle(二分答案+主席树)

  与中位数有关的题二分答案是很常用的trick。二分答案之后,将所有大于它的看成1小于它的看成-1,那么只需要判断是否存在满足要求的一段和不小于0。

  由于每个位置是1还是-1并不固定,似乎不是很好算。考虑暴力一点的想法:对于每一种答案预处理。这样查询就很好办了,线段树上每个区间维护最大前缀和后缀和及总和即可。并且可以发现按答案从小到大考虑的话每个位置都是开始一段为1之后为-1,总修改次数只有n次,建主席树即可。

#include<iostream> 
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
int read()
{
    int x=0,f=1;char c=getchar();
    while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
    while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
    return x*f;
}
#define N 20010
int n,m,a[N],b[N],root[N],lastans,cnt=0; 
vector<int> p[N];
struct data{int l,r,sum,pre,suf;
}tree[N<<7];
void build(int &k,int l,int r)
{
    k=++cnt;tree[k].sum=tree[k].pre=tree[k].suf=r-l+1;
    if (l==r) return;
    int mid=l+r>>1;
    build(tree[k].l,l,mid);
    build(tree[k].r,mid+1,r);
}
void modify(int &k,int l,int r,int x)
{
    tree[++cnt]=tree[k];k=cnt;
    tree[k].sum-=2;
    if (l==r) {tree[k].pre=tree[k].suf=-1;return;}
    int mid=l+r>>1;
    if (x<=mid)    modify(tree[k].l,l,mid,x);
    else modify(tree[k].r,mid+1,r,x);
    tree[k].pre=max(tree[tree[k].l].pre,tree[tree[k].l].sum+tree[tree[k].r].pre);
    tree[k].suf=max(tree[tree[k].r].suf,tree[tree[k].r].sum+tree[tree[k].l].suf);
}
int querysum(int k,int l,int r,int x,int y)
{
    if (x>y) return 0;
    if (l==x&&r==y) return tree[k].sum;
    int mid=l+r>>1;
    if (y<=mid) return querysum(tree[k].l,l,mid,x,y);
    else if (x>mid) return querysum(tree[k].r,mid+1,r,x,y);
    else return querysum(tree[k].l,l,mid,x,mid)+querysum(tree[k].r,mid+1,r,mid+1,y);
}
int querypre(int k,int l,int r,int x,int y)
{
    if (l==x&&r==y) return tree[k].pre;
    int mid=l+r>>1;
    if (y<=mid) return querypre(tree[k].l,l,mid,x,y);
    else if (x>mid) return querypre(tree[k].r,mid+1,r,x,y);
    else return max(querypre(tree[k].l,l,mid,x,mid),querysum(tree[k].l,l,mid,x,mid)+querypre(tree[k].r,mid+1,r,mid+1,y));
}
int querysuf(int k,int l,int r,int x,int y)
{
    if (l==x&&r==y) return tree[k].suf;
    int mid=l+r>>1;
    if (y<=mid) return querysuf(tree[k].l,l,mid,x,y);
    else if (x>mid) return querysuf(tree[k].r,mid+1,r,x,y);
    else return max(querysuf(tree[k].r,mid+1,r,mid+1,y),querysum(tree[k].r,mid+1,r,mid+1,y)+querysuf(tree[k].l,l,mid,x,mid));
}
int calc(int k,int a,int b,int c,int d)
{
    return querysum(root[k],1,n,b+1,c-1)+querysuf(root[k],1,n,a,b)+querypre(root[k],1,n,c,d);
}
int main()
{
#ifndef ONLINE_JUDGE
    freopen("bzoj2653.in","r",stdin);
    freopen("bzoj2653.out","w",stdout);
    const char LL[]="%I64d\n";
#else
    const char LL[]="%lld\n";
#endif
    n=read();
    for (int i=1;i<=n;i++) b[i]=a[i]=read();
    sort(b+1,b+n+1);
    int t=unique(b+1,b+n+1)-b;
    for (int i=1;i<=n;i++)
    {
        a[i]=lower_bound(b+1,b+t,a[i])-b;
        p[a[i]].push_back(i);
    }
    build(root[1],1,n);
    for (int i=2;i<t;i++)
    {
        root[i]=root[i-1];
        for (int j=0;j<p[i-1].size();j++)
        modify(root[i],1,n,p[i-1][j]);
    }
    m=read();
    while (m--)
    {
        int q[4]={(read()+lastans)%n,(read()+lastans)%n,(read()+lastans)%n,(read()+lastans)%n};
        sort(q,q+4);
        int l=1,r=t-1,ans=0;
        while (l<=r)
        {
            int mid=l+r>>1;
            if (calc(mid,q[0]+1,q[1]+1,q[2]+1,q[3]+1)>=0) ans=mid,l=mid+1;
            else r=mid-1;
        }
        printf("%d\n",lastans=b[ans]);
    }
    return 0;
}

 

posted @ 2018-09-03 16:06  Gloid  阅读(94)  评论(0编辑  收藏  举报