zoukankan      html  css  js  c++  java
  • 题解 AT4867 【[ABC155D] Pairs】

    题目
    两次二分
    首先对ans进行二分,在([-10^{18},10^{18}])之间
    考虑怎么check
    对于每个ans,枚举每个(a_i),二分查找有几个(a_j),使得(a_i imes a_j<ans)
    对于每对合法的((a_i,a_j)),枚举到(a_i)(a_j)的时候都被找了一遍,所以最后应该除以2来去重
    因为不能出现(a_i=a_j),所以返回的时候要把(i=j)的情况去掉
    两个数组a,b
    将输入的数在a中按升序排序,在b中按降序排
    在第二次二分时,当(a_i<0),用b数组二分(负负得正,所以此时绝对值更大的负时是“大”的,因为它与(a_i)的乘积更大),当(a_igeq 0),用a数组,这样保证了可二分性
    通过第一层二分,我们找到的其实是在n个(a_i)两两相乘得到的数中,比从小到大第k个数大的最小数,所以要输出(ans-1)
    复杂度(O(nlog nlog 10^{18}))
    code.

    #include<cstdio>
    #include<algorithm>
    #include<iostream>
    #include<cmath>
    #include<iomanip>
    #include<cstring>
    #define R register
    #define EN std::puts("")
    #define LL long long
    inline LL read(){
    	LL x=0,y=1;
    	char c=std::getchar();
    	while(c<'0'||c>'9'){if(c=='-') y=0;c=std::getchar();}
    	while(c>='0'&&c<='9'){x=x*10+(c^48);c=std::getchar();}
    	return y?x:-x;
    }
    //a:abs大的负数->abs小的负数->小正数->大正数
    //b:大正数->小正数->abs小的负数->abs大的负数 
    int n,fir;//a[fir]是a数组中第一个非负数
    LL k;
    LL a[200006],b[200006];
    inline int finda(int i,LL ans){
    	R int l=1,r=n,mid,ret=0;
    	while(l<=r){
    		mid=(l+r)>>1;
    		if(a[mid]*a[i]<ans) ret=mid,l=mid+1;
    		else r=mid-1;
    	}
    	return ret>=i?(ret-1):ret;//如果ret>=i,说明其中有一对是两个a[i]相乘得到,此时要减1
    }
    inline int findb(int i,LL ans){
    	R int l=1,r=n,mid,ret=0;
    	while(l<=r){
    		mid=(l+r)>>1;
    		if(b[mid]*a[i]<ans) ret=mid,l=mid+1;
    		else r=mid-1;
    	}
    	return (ret+i>n)?(ret-1):ret;//同理,但b是倒序
    }
    inline int check(LL ans){
    	LL nowk=0;
    	for(R int i=1;i<fir;i++)//负数 
    		nowk+=findb(i,ans);
    	for(R int i=fir;i<=n;i++)//正数
    		nowk+=finda(i,ans);
    	nowk/=2;//去重
    	return nowk>=k;
    }
    inline int cmp(int x,int y){
    	return x<y;
    }
    int main(){
    	n=read();k=read();
    	for(R int i=1;i<=n;i++) a[i]=read(); 
    	std::sort(a+1,a+1+n,cmp);
    	fir=n+1;
    	for(R int i=1;i<=n;i++)
    		if(a[i]>=0){fir=i;break;}
    	for(R int i=1;i<=n;i++) b[n-i+1]=a[i];//存入b数组,对负数二分时使用
    	R LL l=-1e18,r=1e18,mid,ans;
    	while(l<=r){
    		mid=(l+r)/2;
    		if(check(mid)) ans=mid,r=mid-1;
    		else l=mid+1;
    	}
    	std::printf("%lld",ans-1);
    	return 0;
    }
    
  • 相关阅读:
    group_concat的长度限制
    mb_strlen默认字符集问题
    &符号导致的一个bug
    python面向对象编程系列
    python基础之面向过程编程系列
    RPA流程自动化
    什么是DevOps?
    ansible详解
    saas和paas的区别
    CPT/cpt接口
  • 原文地址:https://www.cnblogs.com/suxxsfe/p/12527510.html
Copyright © 2011-2022 走看看