2020 CCPC Wannafly Winter Camp Day1 Div.1&2(F 乘法)(二分)

2020 CCPC Wannafly Winter Camp Day1 Div.1&2(F 乘法)(二分)

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld

题目描述

给出一个长度为 n n 的数列 A 1 , , A n A_1, \dots, A_n 和一个长度为 m m 的数列 B 1 , , B m B_1, \dots, B_m ,可以构造得到一个 n × m n \times m 的矩阵 C C ,其中 C i , j = A i × B j C_{i,j} = A_i \times B_j
给出整数 K K ,你需要求出 C C 中第 K K 大的数的值。

输入描述:

第一行输入三个整数 n , m , K ( 1 n , m 1 0 5 , 1 K n × m ) n,m,K(1 \leq n,m \leq 10^5, 1\leq K \leq n \times m)
第二行输入 n n 个空格隔开的整数 A 1 , . . . , A n ( 1 0 6 A i 1 0 6 ) A_1,..., A_n(-10^6 \leq A_i \leq 10^6)
第三行输入 m m 个空格隔开的整数 B 1 , . . . , B m ( 1 0 6 B i 1 0 6 ) B_1,..., B_m (-10^6 \leq B_i \leq 10^6)

输出描述:

输出一行一个整数,表示矩阵中的第 K K 大的数的值。

示例1

输入

3 3 3
2 3 4
4 5 6

输出

18

题解

第K小分数(二分) 很相似的一道题,不过这个更复杂了些,矩阵里可以有 0 0 和负数,题解里对负数做了讨论,不过我却看到了大神更巧妙的写法。。。(原谅我菜的过分没考虑负数这玩意)

官方题解:
在这里插入图片描述

需要注意官方题解的逻辑是以 1 0 18 10^{18} 为中心进行二分,而 m i d 1 0 18 mid-10^{18} 也就相当于以 0 0 为中心进行二分,跟一般写法无异,还有就是官方题解是计算的比检验值小的个数,因为之前已经把 k k 转换成第 k k 小的数字了(见于代码第43行)。

代码

题解代码

#include <iostream>
#include <cmath>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <cassert>
#include <map>
#include <vector>
using namespace std;
int n, m;
long long K;
const int N = 110000;
vector<int> A[3], B[3];
long long get(long long lim) {
	if (lim >= 0) {
		long long ans = (long long)(A[1].size())*m + (long long)(B[1].size())*(n - A[1].size());
		ans += 1ll * A[0].size()*B[2].size() + 1ll * A[2].size()*B[0].size();
		for (int a = 0; a <= 2; a += 2) {
			int b = a;
			int now = int(B[b].size()) - 1;
			for (int i = 0; i < A[a].size(); ++i) {
				while (now >= 0 && 1ll * A[a][i] * B[b][now] > lim) now--;
				ans += now + 1;
			}
		}
		return ans;
	}
	else if (lim < 0) {
		long long ans = 0;
		for (int a = 0; a <= 2; a += 2) {
			int b = 2 - a;
			int now = int(B[b].size()) - 1;
			for (int i = 0; i < A[a].size(); ++i) {
				while (now >= 0 && 1ll * A[a][i] * B[b][now] >= lim) now--;
				ans += int(B[b].size()) - now - 1;
			}
		}
		return ans;
	}
}
int main() {
	scanf("%d%d%lld", &n, &m, &K);
	K = 1ll * n*m - K + 1;
	for (int i = 1; i <= n; i++) {
		int k1; scanf("%d", &k1);
		if (k1 > 0) A[0].push_back(k1);
		else if (k1 == 0) A[1].push_back(k1);
		else A[2].push_back(-k1);
	}
	for (int i = 1; i <= m; i++) {
		int k1; scanf("%d", &k1);
		if (k1 > 0) B[0].push_back(k1);
		else if (k1 == 0) B[1].push_back(k1);
		else B[2].push_back(-k1);
	}
	for (int i = 0; i < 3; i++) {
		sort(A[i].begin(), A[i].end());
		sort(B[i].begin(), B[i].end());
	}
	long long l = 0, r = 2e18, ans = 0, bias = 1e18;
	while (l < r) {
		long long mid = (l + r >> 1);
		if (get(mid - bias) >= K) {
			ans = mid - bias; r = mid;
		}
		else l = mid + 1;
	}
	cout << ans << endl;
}

大神代码

#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#define LL long long
using namespace std;
const int maxn = 1e5 + 5;

LL a[maxn], b[maxn];
LL n, m, k;

bool che(LL mid) {
	LL num = 0;
	for (int i = 1; i <= m; i++) {
		if (b[i] == 0) num += mid < 0 ? n : 0;
		if (b[i] < 0) num += lower_bound(a + 1, a + n + 1, ceil((double)mid / b[i])) - (a + 1);
		if (b[i] > 0) num += n - ((upper_bound(a + 1, a + n + 1, floor((double)mid / b[i]))) - (a + 1));
	}
	return num <= k;
}

int main() {
	cin >> n >> m >> k;
	k--;
	for (int i = 1; i <= n; i++) scanf("%lld", &a[i]);
	for (int i = 1; i <= m; i++) scanf("%lld", &b[i]);
	sort(a + 1, a + n + 1);
	sort(b + 1, b + m + 1);
	LL l = -1e13, r = 1e13;
	while (l + 1 < r) {
		LL mid = (l + r) >> 1;
		if (che(mid))r = mid;
		else l = mid;
	}
	cout << r << endl;
	return 0;
}
发布了163 篇原创文章 · 获赞 54 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/weixin_42856843/article/details/104063141
今日推荐