zoukankan      html  css  js  c++  java
  • [BZOJ 2653] middle

    Link:

     BZOJ 2653 传送门

    Solution:

    针对中位数问题的特殊处理:

    假设中位数为$x$,那么把小于$x$的赋值−1,把大于等于$x$的赋值+1

    然后看看是否有连续的一段$sumge 0$,如有则保证能取到这样的$x$

    如果$sum$大于0则证明答案应该更大,相反答案应该更小

    $sum$显然是单调的,所以满足二分性质,考虑二分答案。

    接下来考虑如何维护区间和,

    线段树维护三个值$sum$,$lsum$,$rsum$分别表示区间和,从左端/右端起最大连续子序列和

    满足条件$[a,b][c,d]$的最长连续子序列和即为$sum(b,c)+rsum(a,b-1)+lsum(c+1,d)$

    但明显不可能对所有的值都单独构建一棵线段树,于是我们想到了主席树

    想要有可重用的部分,就要使得建树的顺序具有单调性:

    于是我们将原数列排序,先将线段树上所有点都赋为1

    然后让第$i$个棵树在第$i-1$棵树的基础上增加一条第$i-1$个点为-1的链即可

     

    Code:

    #include <bits/stdc++.h>
    
    using namespace std;
    #define NOW seg[cur]
    #define LC NOW.ls
    #define RC NOW.rs
    
    inline int read()
    {
        char ch;int num,f=0;
        while(!isdigit(ch=getchar())) f|=(ch=='-');
        num=ch-'0';
        while(isdigit(ch=getchar())) num=num*10+ch-'0';
        return f?-num:num;
    }
    
    template<class T> inline void putnum(T x)
    {
        if(x<0)putchar('-'),x=-x;
        register short a[20]={},sz=0;
        while(x)a[sz++]=x%10,x/=10;
        if(sz==0)putchar('0');
        for(int i=sz-1;i>=0;i--)putchar('0'+a[i]);
        putchar('
    ');
    }
    
    const int MAXN=1e6;
    
    struct FunTree
    {
        int ls,rs;
        int sum,lsum,rsum;
    }seg[MAXN];
    int n,dat[MAXN],id[MAXN],root[MAXN],cnt=0;
    
    bool cmp(int x,int y)
    {
        return dat[x]<dat[y];
    }
    
    void Update(int cur)
    {
        NOW.sum=seg[LC].sum+seg[RC].sum;
        NOW.lsum=max(seg[LC].lsum,seg[LC].sum+seg[RC].lsum);
        NOW.rsum=max(seg[RC].rsum,seg[RC].sum+seg[LC].rsum);
    }
    
    void Build_Tree(int& cur,int l,int r)
    {
        cur=++cnt;
        if(l==r){NOW.sum=NOW.lsum=NOW.rsum=1;return;}
        int mid=(l+r)/2;
        Build_Tree(LC,l,mid);Build_Tree(RC,mid+1,r);
        Update(cur);
    }
    
    void Insert(int pre,int& cur,int pos,int val,int l,int r)
    {
        cur=++cnt;
        if(l==r){NOW.sum=NOW.lsum=NOW.rsum=-1;return;}
        
        int mid=(l+r)>>1;NOW=seg[pre];
        if(pos<=mid) Insert(seg[pre].ls,NOW.ls,pos,val,l,mid);
        else Insert(seg[pre].rs,NOW.rs,pos,val,mid+1,r);
        Update(cur);
    }
    
    int Query(int a,int b,int cur,int l,int r)
    {
        if(a<=l && r<=b) return NOW.sum;
        int mid=(l+r)>>1,ret=0;
        
        if(a<=mid) ret+=Query(a,b,NOW.ls,l,mid);
        if(b>mid) ret+=Query(a,b,NOW.rs,mid+1,r);
        return ret; 
    }
    
    int Left(int a,int b,int cur,int l,int r)  //注意求Left和Right时与求Sum的不同
    {
        if(a<=l && r<=b) return NOW.lsum;
        
        int mid=(l+r)>>1;
        if(b<=mid) return Left(a,b,NOW.ls,l,mid);
        if(a>mid) return Left(a,b,NOW.rs,mid+1,r);
        return max(Left(a,mid,NOW.ls,l,mid),Query(a,mid,NOW.ls,l,mid)+Left(mid+1,b,NOW.rs,mid+1,r));
    }
    
    int Right(int a,int b,int cur,int l,int r)
    {
        if(a<=l && r<=b) return NOW.rsum;
        
        int mid=(l+r)>>1;
        if(a>mid) return Right(a,b,NOW.rs,mid+1,r);
        if(b<=mid) return Right(a,b,NOW.ls,l,mid);
        return max(Right(mid+1,b,NOW.rs,mid+1,r),Query(mid+1,b,NOW.rs,mid+1,r)+Right(a,mid,NOW.ls,l,mid));
    }
    
    bool check(int x,int a,int b,int c,int d)
    {
        int ret1=Query(b,c,root[x],1,n);
        int ret2=max(Left(c+1,d,root[x],1,n),0);
        int ret3=max(Right(a,b-1,root[x],1,n),0);
        return ret1+ret2+ret3>=0;
    }
    
    int main()
    {
        n=read();
        for(int i=1;i<=n;i++) dat[i]=read(),id[i]=i;
        sort(id+1,id+n+1,cmp);sort(dat+1,dat+n+1);
        Build_Tree(root[1],1,n);
        
        for(int i=2;i<=n;i++) Insert(root[i-1],root[i],id[i-1],-1,1,n);
        
        int T=read(),last=0;
        while(T--)
        {
            int q[5];
            for(int i=1;i<=4;i++)
                q[i]=(read()+last)%n+1;
            sort(q+1,q+5);
            
            int l=1,r=n;
            while(l<=r) //二分答案
            {
                int mid=(l+r)>>1;
                if(check(mid,q[1],q[2],q[3],q[4])) l=mid+1;
                else r=mid-1;
            }
            putnum(last=dat[r]);
        }
        return 0;
    }

    Review:

    1、针对中位数的套路:

    把小于$x$的赋值−1,把大于等于$x$的赋值+1,然后看看是否有连续的一段$Sum$>=0

      

    2、主席树建树时要使其顺序具有一定单调性

    3、注意对查找$Left$、$Right$时和查找$Sum$时的区别

  • 相关阅读:
    ORM之F和Q
    ORM查询
    Django
    jQuery基础
    DOM和BOM
    saas baas paas iaas 的理解
    分布式架构的演进过程
    tomcat 配置https 证书
    idea 学习总结
    简单数据库连接池-总结
  • 原文地址:https://www.cnblogs.com/newera/p/9081484.html
Copyright © 2011-2022 走看看