CF1665E MinimizOR

Key Observation:小于 \(2^k\) 的数两两取或的最小值一定由前 \(k + 1\) 小的一对组成。

证明采用数学归纳法:

  • 对于 \(k = 1\),显然成立。

  • 假设对于 \(k\) 结论成立,那么下面证明对于 \(k + 1\) 也成立。

    • 对于所有数的第 \(k\) 位都是 \(1\),那么答案的第 \(k\) 位一定是 \(1\),于是只要考虑 \(k - 1\) 位组成的最小值,所以需要 \(k + 1\) 个数。
    • 对于所有数中有超过 \(2\) 个数的第 \(k\) 位为 \(0\),那么答案第 \(k\) 位一定为 \(0\),只要考虑 \(k - 1\) 位组成的最小值,同理需要 \(k + 1\) 个数。
    • 对于所有数中只有一个第 \(k\) 位为 \(0\),那么答案第 \(k\) 位一定为 \(1\),我们只要考虑第 \(k\) 位上为 \(1\)\(k + 1\) 个数,然后再加上一个第 \(k\) 位为 \(0\) 的数,需要 \(k + 2\) 个数。

证毕。

于是用线段树维护最小值,每次查前 \(31\) 小值,暴力求解。时间复杂度为 \(\mathcal{O}(Tn \log n + Tq \cdot 31^2 \log n)\),慢出大便了。

#include <bits/stdc++.h>
#define fi first
#define se second
#define mp std::make_pair
typedef long long ll;
#define gc() (_S == _T && (_T = (_S = _B) + fread(_B, 1, 1 << 20, stdin), _S == _T) ? EOF : *_S++)
char _B[1 << 20], *_S = _B, *_T = _B;
int rd() {
	int x = 0, f = 0; char ch = gc();
	while(ch < '0' || ch > '9') (ch == '-') && (f = 1), ch = gc();
	while(ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = gc();
	return f ? -x : x;
}
const int N = 1e5 + 10;
int n, q, a[N], cnt = 0;
struct node {
	int l, r;
	std::pair<int, int> v;
	node *ls, *rs;
} tr[N << 2], *rt;
std::pair<int, int> ret[32];
void pushup(node *o) {
	o -> v = std::min(o -> ls -> v, o -> rs -> v);
}
void build(node *o, int l, int r) {
	o -> l = l, o -> r = r;
	if(l == r) {
		o -> v = mp(a[l], l);
		return;
	}
	int mid = (l + r) >> 1;
	o -> ls = &tr[++cnt], o -> rs = &tr[++cnt];
	build(o -> ls, l, mid), build(o -> rs, mid + 1, r), pushup(o);
}
void upd(node *o, int p, int k) {
	if(o -> l == o -> r) {
		o -> v = mp(k, p);
		return;
	}
	int mid = (o -> l + o -> r) >> 1;
	if(p <= mid) upd(o -> ls, p, k);
	else upd(o -> rs, p, k);
	pushup(o);
}
std::pair<int, int> qry(node *o, int ql, int qr) {
	if(ql <= o -> l && o -> r <= qr) return o -> v;
	int mid = (o -> l + o -> r) >> 1;
	std::pair<int, int> ret = mp(1 << 30, 1 << 30);
	if(ql <= mid) ret = std::min(ret, qry(o -> ls, ql, qr));
	if(qr > mid) ret = std::min(ret, qry(o -> rs, ql, qr));
	return ret;
}
void solve() {
	n = rd();
	for(int i = 1; i <= n; ++i) a[i] = rd();
	cnt = 0, rt = &tr[0], build(rt, 1, n);
	q = rd();
	int tot = 0;
	for(int _ = 1; _ <= q; ++_) {
		int l = rd(), r = rd(), up = std::min(31, r - l + 1);
		tot = 0, memset(ret, 0, sizeof(ret));
		for(int i = 1; i <= up; ++i) ret[++tot] = qry(rt, l, r), 
			upd(rt, ret[tot].se, 1 << 30);
		int ans = 1 << 30;
		for(int i = 1; i <= tot; ++i) for(int j = i + 1; j <= tot; ++j)
			ans = std::min(ans, ret[i].fi | ret[j].fi);
		printf("%d\n", ans);
		for(int i = 1; i <= tot; ++i) upd(rt, ret[i].se, a[ret[i].se]);
	}
}
int main() {
	for(int _ = rd(); _; _--) solve();
	return 0;
}
posted @ 2022-05-02 20:49  Nylch  阅读(33)  评论(0编辑  收藏  举报