ZOJ 3278 8G Island (二分套二分)

俗话说二分很难,真的很难,,,很难。(我会说这道题解一晚上么)

题目让求两数列乘积的n*m个数中第k大。

思路就是在[min,max]的范围内二分答案,然后对于每个二分的中点mid,枚举a[i],二分求出对于每个a[i],有几个b[j]使得a[i]*b[j]>=mid。然后把统计的个数全都加起来就是mid在n*m个数中的排名。

我们要找的就是最大的mid使得n*m个数中大于等于mid的数共有k个

#pragma warning(disable:4996)
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
int n, m;
LL k;
int a[100005], b[100005];

int find(int target, LL midd){//返回左数第一个b[i]使得b[i]*target>=midd的角标i
	int low = 1, high = m;
	while (low + 1 < high){
		int mid = (low + high) >> 1;
		if ((LL)b[mid] * target < midd)low = mid + 1;
		else high = mid;
	}
	if ((LL)b[low] * target >= midd)return low;
	if ((LL)b[high] * target >= midd)return high;
	return m + 1;
}

int main(){
	//freopen("in.txt", "r", stdin);
	while (~scanf("%d", &n)){
		scanf("%d %lld", &m, &k);
		for (int i = 1; i <= n; i++)scanf("%d", a + i);
		for (int j = 1; j <= m; j++)scanf("%d", b + j);
		
		sort(a + 1, a + 1 + n);
		sort(b + 1, b + 1 + m);

		LL low = (LL)a[1] * b[1], high = (LL)a[n] * b[m];
		while (low + 1< high){
			LL mid = (low + high) >> 1;
			LL rank = 0;
			for (int i = 1; i <= n; i++){
				int index = find(a[i], mid);
				rank += m + 1 - index;
			}
			//rank是n*m个数中>=mid的个数
			if (rank < k)high = mid - 1;
			else if (rank>k)low = mid + 1;
			else low = mid;
		}

		LL ans = -1;
		for (int i = 1; i <= n; i++){
			int u = find(a[i], low);
			int v = find(a[i], high);
			if (low%a[i] == 0 && u <= m&&b[u] * (LL)a[i] == low){
				ans = low;
				break;
			}
			if (high%a[i] == 0 && v <= m&&b[v] * (LL)a[i] == high){
				ans = high;
				break;
			}
		}

		printf("%lld\n", ans);

	}
	return 0;
}


你可能感兴趣的:(ZOJ 3278 8G Island (二分套二分))