BZOJ 2653 middle CTSC

Problem

BZOJ

Solution

这道题思路很神,感觉二分答案题愈发得神了起来。

我们考虑如果选择x作为中位数,把序列中所有小于x的数标记为-1,大于的标记为1,那么询问x能否作为一段区间的中位数,就相当于询问是否存在一个区间使得区间和是大于等于0的。显然我们可以用线段树维护出一个最大区间和,那么直接查它是否大于等于0即可。而x是可以二分的。
除此之外对于每个数作为中位数,我们都需要一棵这样的线段树,可以用主席树做。
然后方便起见你也可以只写一个query,用结构体存返回值lmax,rmax,maxsum,每次合并答案即可。
时间复杂度 O ( q l o g 2 n )

Code

#include <algorithm>
#include <cstdio>
using namespace std;
const int maxn=25010,maxm=1000010,INF=0x3f3f3f3f;
struct data{
    int l,r,sum;
    void clear(){l=r=-INF;sum=0;}
}res,t[maxm];
int n,m,tot,l,r,mid,ans,a[maxn],id[maxn],opr[5];
int rt[maxn],lc[maxm],rc[maxm];
inline bool cmp(const int &x,const int &y){return a[x]<a[y];}
data merge(data l,data r)
{
    static data res;
    res.l=max(l.l,l.sum+r.l);res.r=max(r.r,r.sum+l.r);
    res.sum=l.sum+r.sum;
    return res;
}
void build(int l,int r,int& rt)
{
    rt=++tot;
    t[rt].l=t[rt].r=t[rt].sum=r-l+1;
    if(l==r) return ;
    int m=(l+r)>>1;
    build(l,m,lc[rt]);build(m+1,r,rc[rt]);
}
void update(int l,int r,int pos,int val,int& rt)
{
    t[++tot]=t[rt];lc[tot]=lc[rt];rc[tot]=rc[rt];rt=tot;
    if(l==r){t[rt].l=t[rt].r=t[rt].sum=val;return ;}
    int m=(l+r)>>1;
    if(pos<=m) update(l,m,pos,val,lc[rt]);
    else update(m+1,r,pos,val,rc[rt]);
    t[rt]=merge(t[lc[rt]],t[rc[rt]]);
}
void query(int l,int r,int L,int R,int rt)
{
    if(L<=l&&r<=R){res=merge(res,t[rt]);return ;}
    int m=(l+r)>>1;
    if(L<=m) query(l,m,L,R,lc[rt]);
    if(m<R) query(m+1,r,L,R,rc[rt]);
}
int check(int k)
{
    int ret=0;
    if(opr[2]+1<=opr[3]-1)
    {
        res.clear();
        query(1,n,opr[2]+1,opr[3]-1,rt[k]);
        ret+=res.sum;
    }
    res.clear();query(1,n,opr[1],opr[2],rt[k]);ret+=res.r;
    res.clear();query(1,n,opr[3],opr[4],rt[k]);ret+=res.l;
    return ret>=0;
}
int main()
{
    #ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
    #endif
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]),id[i]=i;
    sort(id+1,id+n+1,cmp);build(1,n,rt[1]);
    for(int i=2;i<=n;i++){rt[i]=rt[i-1];update(1,n,id[i-1],-1,rt[i]);}
    scanf("%d",&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d%d",&opr[1],&opr[2],&opr[3],&opr[4]);
        opr[1]=(opr[1]+ans)%n+1;opr[2]=(opr[2]+ans)%n+1;
        opr[3]=(opr[3]+ans)%n+1;opr[4]=(opr[4]+ans)%n+1;
        sort(opr+1,opr+5);
        l=1;r=n;
        while(l<=r)
        {
            mid=(l+r)>>1;
            if(check(mid)) ans=a[id[mid]],l=mid+1;
            else r=mid-1;
        }
        printf("%d\n",ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/as_a_kid/article/details/80137231