bzoj2653

二分+主席树

个人感觉这道题非常好,思维难度高,代码好写(然而自己写的时候神志不清,转态全无qwq)

首先要知道一个关于中位数的基本套路,将大于等于它的赋值成1,小于它的赋值成-1

然后通过区间和就能找出中位数了

对于本题来说:

首先显然答案满足单调性,因为二分的是第k大的数,所以1,-1这个序列和肯定是单调变化的

其次我们考虐将[a,b],[c,d]区间转化成[a,b],(b,c),[c,d]

所以我们只需查询[a,b]中的最大右连续子段和(必选一个),(b,c)的区间和,[c,d]的最大左连续子段和(必选一个)

若这三个和>=0,则说明当前二分的第k大的数是ok的,否则不行,然后继续二分

对于维护每个数作为中位数时的1,-1序列,我们用主席树维护一下就ok了

效率:O(n(logn)^3)

/**************************************************************

    Problem: 2653

    User: syh0313

    Language: C++

    Result: Accepted

    Time:2156 ms

    Memory:12788 kb

****************************************************************/

 

#include <iostream>

#include <cstdio>

#include <cstdlib>

#include <cstring>

#include <string>

#include <cmath>

#include <algorithm>

#include <map>

#define lch a[n].lc

#define rch a[n].rc

using namespace std;

const int maxn=20010;

int n,m,v[maxn],vv[maxn],root[maxn],linum,line[maxn],topt,lim,t;

int cnt,to[maxn],st[maxn],nt[maxn],q[5],ans;

struct da{int lc,rc,sum,suml,sumr,l,r;}a[maxn*20];

map<int,int>lc;

void add(int x,int y)

{to[++cnt]=y; nt[cnt]=st[x]; st[x]=cnt;}

void updata(int n)

{

    a[n].sum=a[lch].sum+a[rch].sum;

    a[n].suml=max(a[lch].suml,a[lch].sum+a[rch].suml);

    a[n].sumr=max(a[rch].sumr,a[rch].sum+a[lch].sumr);

}

void build_tree(int &n,int l,int r)

{

    n=++topt; a[n].l=l; a[n].r=r;

    if (l==r)

    {

        a[n].sum=a[n].suml=a[n].sumr=1;

        return;

    }

    int mid=(l+r)>>1;

    build_tree(lch,l,mid); build_tree(rch,mid+1,r);

    updata(n);

}

void tree_add(int old,int &n,int l,int r,int lc)

{

    if (n<=lim) n=++topt;

    if (l==r) {a[n].sum=a[n].suml=a[n].sumr=-1; return;}

    int mid=(l+r)>>1;

    if (lc<=mid)

    {

        if (rch<=lim) rch=a[old].rc;

        tree_add(a[old].lc,lch,l,mid,lc);

    }

    else

    {

        if (lch<=lim) lch=a[old].lc;

        tree_add(a[old].rc,rch,mid+1,r,lc);

    }

    updata(n);

}

int qsum(int n,int L,int R,int l,int r)

{

    if (l>r) return 0;

    if (L==l && R==r) return a[n].sum;

    int mid=(L+R)>>1;

    if (r<=mid) return qsum(lch,L,mid,l,r);

    else if (l>=mid+1) return qsum(rch,mid+1,R,l,r);

    else return qsum(lch,L,mid,l,mid)+qsum(rch,mid+1,R,mid+1,r);

}

int qsuml(int n,int L,int R,int l,int r,int k)

{

    if (L==l && R==r) return a[n].suml;

    int mid=(L+R)>>1;

    if (r<=mid) return qsuml(lch,L,mid,l,r,k);

    else if (l>=mid+1) return qsuml(rch,mid+1,R,l,r,k);

    else

    {

        int kk=qsum(root[k],0,t,l,mid)+qsuml(rch,mid+1,R,mid+1,r,k);

        return max(kk,qsuml(lch,L,mid,l,mid,k));

    }

}

int qsumr(int n,int L,int R,int l,int r,int k)

{

    if (L==l && R==r) return a[n].sumr;

    int mid=(L+R)>>1;

    if (r<=mid) return qsumr(lch,L,mid,l,r,k);

    else if (l>=mid+1) return qsumr(rch,mid+1,R,l,r,k);

    else

    {

        int kk=qsum(root[k],0,t,mid+1,r)+qsumr(lch,L,mid,l,mid,k);

        return max(kk,qsumr(rch,mid+1,R,mid+1,r,k));

    }

}

int main()

{

    scanf("%d",&n); t=n-1;

    for (int i=0;i<n;i++) scanf("%d",&v[i]),vv[i]=v[i];

    sort(vv,vv+n);

    for (int i=0;i<n;i++) if (!lc[vv[i]]) lc[vv[i]]=++linum,line[linum]=vv[i];

    for (int i=0;i<n;i++) add(lc[v[i]],i);

    build_tree(root[1],0,n-1);

    for (int i=2;i<=linum;i++)

    {

        int p=st[i-1]; lim=topt;

        while (p) {tree_add(root[i-1],root[i],0,n-1,to[p]); p=nt[p];}

    }

    scanf("%d",&m);

    while (m--)

    {

        for (int i=1;i<=4;i++) scanf("%d",&q[i]),q[i]=(q[i]+ans)%n;

        sort(q+1,q+5); int l=1,r=linum; ans=0;

        while (l<=r)

        {

            int mid=(l+r)>>1;

            int ss=qsum(root[mid],0,n-1,q[2]+1,q[3]-1);

            ss+=qsumr(root[mid],0,n-1,q[1],q[2],mid);

            ss+=qsuml(root[mid],0,n-1,q[3],q[4],mid);

            if (ss>=0) {ans=max(ans,mid); l=mid+1;}else r=mid-1;

        }

        ans=line[ans];

        printf("%d\n",ans);

    }

return 0;

}

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值