• [国家集训队] middle


    ( ext{Description})

    传送门

    ( ext{Solution})

    首先如果让你求 ([l,r]) 区间的中位数是很好算的:具体就是搞一棵主席树,然后二分中位数并算出它在区间的排名用于检验。

    可这道题是不定区间,我们二分的中位数实际上算不出排名。但题目需要最大化中位数:也就是说如果我们二分一个 (mid),我们选择的区间需要使大于 (mid) 的值尽量多,这样就越可能使 (mid) 变大(我们先不考虑与 (mid) 相同的数)。

    那么问题就转化成二分一个 (mid),求出所有左端点在 ([a,b]) 之间,右端点在 ([c,d]) 之间的区间的大于 (mid) 的值的个数 (-) 小于 (mid) 的值的个数(令这个值为 (val))的最大值。如果这个最大值 (ge 0)(mid) 可能是中位数,增大 (mid),反之减小 (mid)

    ([b+1,c-1]) 里面的数肯定要取,那么接着考虑的就是找出 (x,y),使 ([x,b],[c,y]) 区间的 (val) 的和最大。

    其实那两个区间我们分别知道了右端点和左端点,其实就是求后缀/前缀最大。

    如何求 (val)?有个很经典的套路:在原序列将比 (mid) 小的数的位置赋值为 (-1),大于 (mid)(mid) 本身的位置赋值为 (1)。这样查询 ([l,r]) 的权值和就是 (val) 值。

    那么 (val) 显然用线段树维护。不过还有一个问题:对于每种权值,显然之前的 (-1/1) 赋值是不一样的。

    难道对于每个权值建一棵线段树(即 (n) 棵线段树)?

    显然时空都无法承受(动态开点也不行,因为是查询区间)。

    我们注意到对于相邻的两种权值 (x<y)(y) 相对于 (x) 只是 (x) 对应的下标的赋值从 (1) 变成 (-1)

    所以用可持久化线段树就行了,空间复杂度 (nlog n),时间复杂度 (mathcal O(Qlog^2n))

    ( ext{Q&A})

    ( ext Q1):不会出现二分的数不在区间内吗?

    ( ext A1):中位数看的是下标,显然区间会有大于二分值的数满足这个条件,我们继续往上调二分值,一定会在区间内。

    ( ext Q2):如何处理权值相等的情况?

    ( ext A2):将权值排序后,可以直接强制权值相等的大小关系。为什么是对的?首先这不影响权值与不同权值的大小关系,其次二分是一定可以分到正确的那个位置的。

    ( ext{Code})

    #include <cstdio>
    
    #define rep(i,_l,_r) for(register signed i=(_l),_end=(_r);i<=_end;++i)
    #define fep(i,_l,_r) for(register signed i=(_l),_end=(_r);i>=_end;--i)
    #define erep(i,u) for(signed i=head[u],v=to[i];i;i=nxt[i],v=to[i])
    #define efep(i,u) for(signed i=Head[u],v=to[i];i;i=nxt[i],v=to[i])
    #define print(x,y) write(x),putchar(y)
    
    template <class T> inline T read(const T sample) {
        T x=0; int f=1; char s;
        while((s=getchar())>'9'||s<'0') if(s=='-') f=-1;
        while(s>='0'&&s<='9') x=(x<<1)+(x<<3)+(s^48),s=getchar();
        return x*f;
    }
    template <class T> inline void write(const T x) {
        if(x<0) return (void) (putchar('-'),write(-x));
        if(x>9) write(x/10);
        putchar(x%10^48);
    }
    template <class T> inline T Max(const T x,const T y) {if(x>y) return x; return y;}
    template <class T> inline T Min(const T x,const T y) {if(x<y) return x; return y;}
    template <class T> inline T fab(const T x) {return x>0?x:-x;}
    template <class T> inline T gcd(const T x,const T y) {return y?gcd(y,x%y):x;}
    template <class T> inline T lcm(const T x,const T y) {return x/gcd(x,y)*y;}
    
    #include <iostream>
    #include <algorithm>
    using namespace std;
    
    const int maxn=2e4+5;
    
    int n,lastans,val[maxn],p[maxn],rt[maxn],q,num[5];
    
    namespace PDT {
    	int son[maxn*20][2],siz;
    
        struct node {
            int lmax,rmax,sum;
    
            node operator + (node t) {
                return (node) {
                    Max(lmax,sum+t.lmax),
                    Max(rmax+t.sum,t.rmax),
                    sum+t.sum
                };
            }
        } t[maxn*20];
    
        void Build(int &o,int l,int r) {
            o=++siz;
            if(l>=r) return (void)(t[o]=(node){1,1,1});
            int mid=l+r>>1;
            Build(son[o][0],l,mid); Build(son[o][1],mid+1,r);
            t[o]=t[son[o][0]]+t[son[o][1]];
        }
    
        void Insert(int &o,int pre,int l,int r,int pos) {
            o=++siz;
            if(l>=r) return (void)(t[o]=(node){-1,-1,-1});
            int mid=l+r>>1;
            if(pos<=mid) son[o][1]=son[pre][1],Insert(son[o][0],son[pre][0],l,mid,pos);
            else son[o][0]=son[pre][0],Insert(son[o][1],son[pre][1],mid+1,r,pos);
            t[o]=t[son[o][0]]+t[son[o][1]];
        }
    
        node Query(int o,int l,int r,int L,int R) {
        	if(L>R) return (node){0,0,0};
            if(l>=L&&r<=R) return t[o];
            int mid=l+r>>1;
            if(R<=mid) return Query(son[o][0],l,mid,L,R);
            if(L>mid) return Query(son[o][1],mid+1,r,L,R);
            return Query(son[o][0],l,mid,L,R)+Query(son[o][1],mid+1,r,L,R);
        }
    }
    
    bool cmp(int x,int y) {
    	return val[x]<val[y];
    }
    
    bool ok(int x) {
    	int ans1=PDT::Query(rt[x],1,n,num[1]+1,num[2]-1).sum;
    	int ans2=PDT::Query(rt[x],1,n,num[0],num[1]).rmax;
    	int ans3=PDT::Query(rt[x],1,n,num[2],num[3]).lmax;
    	return ((ans1+ans2+ans3)>=0);
    }
    
    int main() {
    	n=read(9);
    	rep(i,1,n) val[i]=read(9),p[i]=i;
    	sort(p+1,p+n+1,cmp);
        PDT::Build(rt[1],1,n);
        rep(i,2,n) PDT::Insert(rt[i],rt[i-1],1,n,p[i-1]);
        q=read(9);
        while(q--) {
            rep(i,0,3) num[i]=(lastans+read(9))%n;
            sort(num,num+4);
            rep(i,0,3) ++num[i];
            int l=1,r=n,mid;
            while(l<=r) {
                mid=l+r>>1; 
                if(ok(mid)) lastans=mid,l=mid+1;
                else r=mid-1; 
            }
            print(val[p[lastans]],'
    ');
            lastans=val[p[lastans]];
        }
    	return 0;
    }
    
    
  • 相关阅读:
    springboot整合mybatis实现增删改查小案例
    浅谈Nginx负载均衡原理与实现
    使用Cordova框架把Webapp封装成Hybrid App实践——Android篇
    ActiveMQ结合WebScoket应用例子以及介绍
    个人简介
    C#中的属性,字段,变量
    Aspose.Words 直接写response导出docx文档显示文件已损坏需要修复的解决办法
    System.Web.Security.FormsAuthentication.HashPasswordForStoringInConfigFile(string, string)已过时的解决办法
    Aspose.Words:如何添加另一个WORD文档中的Node对象
    基于CPS变换的尾递归转换算法
  • 原文地址:https://www.cnblogs.com/AWhiteWall/p/14237927.html
Copyright © 2020-2023  润新知