YACS 两数之积 题解

分别考虑原数组 a [ ] a[] a[] 中所有的正数,负数以及 0 的数量:

a [ ] a[] a[] 中正数的数量为 c n t 1 cnt1 cnt1 个,把 a [ ] a[] a[] 中所有正数保存在 b z [ ] bz[] bz[] 数组中,

负数数量为 c n t 2 cnt2 cnt2 个,保存在 b f [ ] bf[] bf[] 数组中,

0 的数量为 c n t 0 cnt0 cnt0 个。


x 1 x1 x1, x 0 x0 x0, x 2 x2 x2 分别为两两相乘之后新生成的 b b b 序列中所有正数,0 ,负数的个数,则:

x 1 = c n t 1 ∗ ( c n t 1 − 1 ) / 2 + c n t 2 ∗ ( c n t 2 − 1 ) / 2 x1=cnt1*(cnt1-1)/2+cnt2*(cnt2-1)/2 x1=cnt1(cnt11)/2+cnt2(cnt21)/2

x 0 = c n t 0 ∗ ( n − 1 ) x0=cnt0*(n-1) x0=cnt0(n1)

x 2 = c n t 1 ∗ c n t 2 x2=cnt1*cnt2 x2=cnt1cnt2


讨论 t 的大小:

  1. t ≤ x 1 t≤x1 tx1 ,则这个第 t t t 大数一定是在 b b b 序列中所有的正数中产生的,

b z [ ] bz[] bz[] 数组升序排序, b f [ ] bf[] bf[] 数组先全部变为正数 ( 两两相乘之后一定为正数,先把每个数变成正数方便计算),然后升序排序。

二分答案找到 b z [ ] bz[] bz[] 所有数字和 b f [ ] bf[] bf[] 数组中所有数字两两相乘之后的第 t t t 大。

  1. x 1 < t ≤ x 1 + x 0 x1<t≤x1+x0 x1<tx1+x0,则这个第 t t t 大数就是 0 。

  2. t > x 1 + x 0 t>x1+x0 t>x1+x0 则这个第 t t t 大数一定是在 x 2 x2 x2 个负数中产生的,

问题转化为找到 b z [ ] bz[] bz[] 数组中所有数字和 b f [ ] bf[] bf[] 数组中所有数字两两相乘后的第 t − ( x 1 + x 0 ) t-(x1+x0) t(x1+x0)

同样通过二分答案找到这个第 t − ( x 1 + x 0 ) t-(x1+x0) t(x1+x0) 大数字, 即是原问题所有数字中的第 t t t 大。

#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
typedef long long ll; 

const int maxn = 1e7+10;

ll bz[maxn]= {0}, bf[maxn]= {0};
ll x=0, n=0, t=0;
ll cnt1=0, cnt0=0, cnt2=0;
ll x1=0, x0=0, x2=0;
ll l=0 , r=0;

namespace IO_ReadWrite {
    #define getchar()(p1==p2&&(p2=(p1=_buf)+fread(_buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
    char _buf[1<<21],*p1=_buf,*p2=_buf;
    char ch;bool ff;
    template <typename T>
    inline void read(T &x){
        x=0;ff=false;ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')ff=true;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
        if(ff)x=~x+1;
        return;
    }
    template <typename T>
    inline void write(T x){
        if(x<0)putchar('-'),x=~x+1;
        if(x>9)write(x/10);
        putchar(x%10+'0');
        return;
    }
    inline void ReadChar(char &c){
        do{c=getchar();}while(ch<'0'||ch>'9');return;
    }
    template <typename T>
    inline void writeln(T x){write(x);putchar('\n');return;}
}
using namespace IO_ReadWrite;

bool judge(ll num) {
	ll tmp = 0;
	for (int i = 0; i < cnt1; i++) {
		ll p = upper_bound(bz, bz+cnt1, num/bz[i])-bz;
		if (p > i) tmp += cnt1-p;
		else tmp += cnt1-i-1;
	}
	for (int i = 0; i < cnt2; i++) {
		ll p = upper_bound(bf, bf+cnt2, num/bf[i])-bf;
		if (p > i) tmp += cnt2-p;
		else tmp += cnt2-i-1;
	}
	return tmp >= t;
}

bool check(ll num) {
	ll tmp = 0;
	for (int i = 0; i < cnt1; i++) {
		tmp += cnt2-(upper_bound(bf,bf+cnt2, floor((double)num/bz[i]))-bf);
	}
	return tmp >= t-(x1+x0);
}

int main() {
	read(n);read(t);
	for (int i = 0; i < n; i++) {
		read(x);
		if (x > 0) bz[cnt1++] = x;
		else if (x == 0) cnt0 ++;
		else bf[cnt2++] = x;
	}
	x1 = cnt1*(cnt1-1)/2+cnt2*(cnt2-1)/2;
	x0 = cnt0*(n-1);x2 = cnt1*cnt2;
	if (t <= x1) {
		for(int i=0; i<cnt2; i++)bf[i]*=-1;
		sort(bz,bz+cnt1);
		sort(bf,bf+cnt2);
		l = min(bz[0]*bz[1], bf[0]*bf[1]);
		r = max(bz[cnt1-1]*bz[cnt1-2], bf[cnt2-1]*bf[cnt2-2]);
		while (l <= r) {
			ll mid = (l+r)>>1;
			if (judge(mid))l = mid+1;
			else r = mid-1;
		}
		writeln(l);
	}
	else if (t > x1 && t <= x1+x0)putchar('0');
	else {
		sort(bz, bz+cnt1);
		sort(bf, bf+cnt2);
		l = bz[0]*bf[0];
		r = bz[cnt1-1]*bf[cnt2-1];
		while (l <= r) {
			ll mid = (l+r)/2;
			if (check(mid))l = mid+1;
			else r = mid-1;
		}
		writeln(l);
	}
	return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值