CF1288D 题解

思路

遇到最小值的最大,首先想到二分答案。check 怎么写呢?因为 m8m\le8,所以我们可以状态压缩,在这里 ti,jt_{i,j} 表示 tit_i 二进制的第 jj 位。如果 ai,jmida_{i,j}\ge midti,j=1t_{i,j}=1,否则 ti,j=0t_{i,j}=0。然后只要判断是否有 tit_i 按位或上 tjt_j 是否等于 2m12^m-1 就行,如果有就把答案更新成 i,ji,j。但是 n2n^2 枚举 i,ji,j 肯定不行,这是因为 tt 中有巨量重复元素。于是,我们可以把 visivis_i 表示第一个 tj=it_j=i 的下标 jj,然后两重循环枚举 02m10\sim2^m-1,这下时间复杂度只有 (2m)2(2^m)^2 了,不会超时。总时间复杂度 log(maxai,j,minai,j)max(nm,22m)\log(\max a_{i,j},\min a_{i,j})\max(nm,2^{2m}),时间够了。

代码

# include <bits/stdc++.h>
using namespace std;
int n, m, a[300005][10], t, x, l = 1e9, r, mid, best1 = 1, best2 = 1, vis[270];
bool check () {
	for (int i = 0; i <= t; ++ i)
		vis[i] = 0;
	for (int i = 1; i <= n; ++ i) {
		x = 0;
		for (int j = 0; j < m; ++ j)
			if (a[i][j] >= mid)
				x |= 1 << j;
		if (! vis[x])
			vis[x] = i;
	}
	for (int i = t; ~i; -- i)
		if (vis[i])
			for (int j = t; ~j; -- j)
				if (vis[j] && (i | j) == t) {
					best1 = vis[i], best2 = vis[j];
					return 1;
				}
	return 0;
}
int main () {
	ios::sync_with_stdio (0);
	cin.tie (0);
	cout.tie (0);
	cin >> n >> m;
	t = (1 << m) - 1;
	for (int i = 1; i <= n; ++ i)
		for (int j = 0; j < m; ++ j)
			cin >> a[i][j], r = max (a[i][j], r), l = min (a[i][j], l);
	while (l <= r) {
		mid = l + r >> 1;
		if (check ())
			l = mid + 1;
		else
			r = mid - 1;
	}
	cout << best1 << ' ' << best2;
	return 0;
}
posted @ 2024-03-19 14:28  Vitamin_B  阅读(8)  评论(0)    收藏  举报  来源