51NOD 1105 第K大的数

数组A和数组B,里面都有n个整数。
数组C共有n^2个整数,分别是:
A[0] * B[0],A[0] * B[1] ...... A[0] * B[n-1]
A[1] * B[0],A[1] * B[1] ...... A[1] * B[n-1]
......
A[n - 1] * B[0],A[n - 1] * B[1] ...... A[n - 1] * B[n - 1]
是数组A同数组B的组合,求数组C中第K大的数。
例如:
A:1 2 3,B:2 3 4。
A与B组合成的C为
A[0] A[1] A[2]
B[0] 2 3 4
B[1] 4 6 8
B[2] 6 9 12
共9个数。


调试日志: 没有弄清二分值在满足条件的情况下是应该尽可能小还是尽可能大


Solution

首先对两数组进行排序
现在解决这一个问题: 给定一个数 (k) 求两两相乘 (n^{2}) 个数中有多少个 (<= k)
类似 (Two Points) 解决
因为已经排序, 所以在确定一组解 (a_{i} * b_{j} <= k) 后, 随着 (i) 的递增, 若继续满足条件 (j) 必定递减
这样便可以在 (O(n)) 的时间内询问一次
二分 (k) 即可得到答案
注意这里是第 (K) 小, 需要转换为第 (K)

然后复习二分
保险起见, 二分时无论写法默认开区间
再保险起见, 二分时用变量 (ans) 记录可行答案, 最后返回 (ans)
这时需要分清二分值在满足条件的情况下是应该尽可能小还是尽可能大
其实不对你就换一个就OK了, 不过我们不能糟蹋这门学科是吧
这题需要找的是 序列中的值 , 故二分的值尽可能小(来满足存在于序列中)

Code

#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
#include<climits>
#define LL long long
using namespace std;
LL RD(){
    LL out = 0,flag = 1;char c = getchar();
    while(c < '0' || c >'9'){if(c == '-')flag = -1;c = getchar();}
    while(c >= '0' && c <= '9'){out = out * 10 + c - '0';c = getchar();}
    return flag * out;
    }
const LL maxn = 100019;
LL num, K;
LL a[maxn], b[maxn];
bool check(LL k){
	LL cnt = 0;
	for(LL i = 1, j = num;i <= num;i++){
		while(a[i] * b[j] > k)j--;
		cnt += j;
		}
	if(cnt < K)return 1;
	return 0;
	}
LL search(LL l, LL r){
	LL ans;
	while(l <= r){
		LL mid = (l + r) >> 1;
		if(check(mid))l = mid + 1;
		else ans = mid, r = mid - 1;
		}
	return ans;
	}
int main(){
	num = RD(), K = RD(), K = (LL)num * num - K + 1;
	for(LL i = 1;i <= num;i++)a[i] = RD(), b[i] = RD();
	sort(a + 1, a + 1 + num), sort(b + 1, b + 1 + num);
	printf("%lld
",search(0, 1e18 + 19));
	return 0;
	}
原文地址:https://www.cnblogs.com/Tony-Double-Sky/p/9745891.html