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;
}