zoukankan      html  css  js  c++  java
  • 差值中位数(二分答案+判定中位数)

    题意:求n个数两两之间的差值的中位数.

    分析:首先(n^2)暴力不难想到吧,两层循环求出所有的差值(一共n*(n-1)/2个),存入一个数组中,然后sort排序,最后直接输出答案.考虑如何美妙地切掉这题?

    基本思路是二分答案.为什么可以二分答案,等下讲如何判定当前二分的答案是否成立的时候,就自然而然明白了.我们直接二分中位数mid的值,那么如何判断我们当前二分的这个中位数mid是否成立呢?

    我们对原序列sort排序,然后for循环枚举每一个数a[i],找到第一个x,使得a[x]-a[i]>mid,然后y+=n-x+1,即计算对于当前的a[i],有多少个数与它的差值大于mid.如果y大于tot=n*(n-1)/2的一半,说明此次mid大了,向更小的mid继续二分.如果y小于tot,则说明此次mid小了,向更大的mid继续二分(从这里应该可以看出答案具有单调性吧,所以可以二分答案).

    值得一提的是,对于每一个a[i],找到第一个x,使得a[x]-a[i]>mid,我们没有必要每一次都让x从1开始,一个一个到n这样去找(这样的话,时间复杂度(n^2log_n)),因为我们的序列a是单调递增的,所以每一次i++之后,a[i]都是在递增的,所以a[x]-a[i]一定又会小于(最多等于)mid,总之,x是不可能需要往回走的,所以每一次a[i]改变之后,x是不需要重置为1的.

    有趣之处在于,对于一个差值数列,如果数量是奇数,中位数就在tot/2+1的下标上,而如果数量是偶数,中位数则是tot/2和tot/2+1两个下标上的数的平均值.按照上述二分方法,我们二分到的数一定是差值数列中已经存在的数,所以在遇到偶数情况时,是可能出错的(因为偶数情况时,中位数可能不在原差值数列中),于是我们需要两次二分,然后取平均值.两次二分,稍稍改动一个边界条件即可.

    LL a[50005];
    int main(){
        LL n=read(),ans1,ans2;
        LL tot=n*(n-1)/2;//差值序列的个数
        for(int i=1;i<=n;i++)a[i]=read();
        sort(a+1,a+n+1);//将原序列sort排序
        LL l=0,r=a[n]-a[1];//注意l的初始值
        while(l<=r){
    		LL mid=(l+r)>>1,x=1,y=0;
    		for(int i=1;i<=n;i++){
    	    	while(x<=n&&a[x]-a[i]<=mid)x++;
    	    	y+=n-x+1;
    		}
    		if(y*2<tot){
    	    	ans1=mid;
    	    	r=mid-1;
    		}
    		else l=mid+1;
        }
        l=0,r=a[n]-a[1];
        while(l<=r){
    		LL mid=(l+r)>>1,x=1,y=0;
    		for(int i=1;i<=n;i++){
    	    	while(x<=n&&a[x]-a[i]<=mid)x++;
    	    	y+=n-x+1;
    		}
    		if(y*2<=tot){//唯一的不同之处
    	    	ans2=mid;
    	    	r=mid-1;
    		}
    		else l=mid+1;
        }
        printf("%lld
    ",(ans1+ans2)/2);
        return 0;
    }
    
    
  • 相关阅读:
    kafka搭建
    kafaka学习笔记
    metastore 简单说明
    二 python并发编程之多进程-理论
    文件指针偏移量
    FTP
    1 并发编程
    操作系统简介
    1 网络编程
    网络编程-osi七层
  • 原文地址:https://www.cnblogs.com/PPXppx/p/10362354.html
Copyright © 2011-2022 走看看