POJ 3579 3685(二分-查找第k大的值)

POJ 3579

题意

双重二分搜索:对列数X计算∣Xi Xj∣组成新数列的中位数

思路

对X排序后,与X_i的差大于mid(也就是某个数大于X_i + mid)的那些数的个数如果小于N / 2的话,说明mid太大了。以此为条件进行第一重二分搜索,第二重二分搜索是对X的搜索,直接用lower_bound实现。

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cmath>
using namespace std;

int N;
int CN2 = 0;
int X[100005];
bool C(int x) { // 验证x作为中位数 x = X[j] - X[i] 是否太小
	int cnt = 0;
	for (int i = 0; i<N; ++i) {
		cnt += N - (lower_bound(X + i, X + N, X[i] + x) - X); // 统计差值>=x的个数
	}
	return cnt > CN2 >> 1;
}
void solve() {
	sort(X, X + N);
	CN2 = N*(N - 1) >> 1;
	int lb = 0, ub = 1000000001;
	while (ub - lb > 1) {
		int mid = (ub + lb) >> 1;
		if (C(mid)) lb = mid; // 中位数过小,半闭半开区间[lb, ub)
		else ub = mid;
	}
	printf("%d\n", lb);
}
int main()
{
	while (scanf("%d", &N) == 1) {
		for (int i = 0; i < N; ++i) scanf("%d", X + i);
		solve();
	}
	return 0;
}

POJ 3685

题意:边为n的方阵中,aij=i^2+100000i+j^2-100000j+i*j,求矩阵的第k大数。

思路:需要注意数据规模,特别是1≤M≤N×N,需要64位变量来存

首先来分析下这个函数f(i,j)

image

可知f(i,j)按行递增(同列),而按列不单调(同行)。

C(x)表示矩阵中<x的元素有多少个。 所以可以一列一列来求有多少个比x小。然后二分求出正好有M−1个数比x小即可。

#include <iostream>
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
typedef long long ll;
const ll MAX = 1e15;
ll T;
ll N, M;
ll f(ll i, ll j) {
	return i*i + 100000 * (i - j) + i*j + j*j;
}
bool C(ll x) { // 验证x是否过小。。。。这里写成int x调了好久= =
	ll cnt = 0; // cnt 为<x的个数
	for (int j = 1; j <= N; ++j) {
		int lb = 0, ub = N + 1; // (lb, ub)
		while (ub - lb > 1) {
			int mid = (lb + ub) >> 1;
			if (f(mid, j) < x) lb = mid; // 半闭半开区间[lb, ub)
			else ub = mid;
		}
		cnt += lb;
	}
	return cnt < M;
}
void solve() {
	ll lb = f(0, N), ub = f(N, 0) + 1;
	while (ub - lb > 1) {
		ll mid = (ub + lb) >> 1;
		if (C(mid)) lb = mid; // 半闭半开区间[lb, ub)
		else ub = mid;
	}
	cout << lb << endl;
}
int main() {
	cin >> T;
	while (T--) {
		cin >> N >> M;
		solve();
	}
	return 0;
}
posted @ 2017-09-10 12:26  demianzhang  阅读(715)  评论(0编辑  收藏  举报