E. Iva & Pav
一道二进制好题,这题可以前缀和或二分解决,这里给出一个纯二进制做法。
显然,与上一个数之后对于当前数来说是非严格递减的,那么对于每个数的每一位我们记录从这个数之后到哪个位置最先变为0。
记 n x t [ i ] [ j ] nxt[i][j] nxt[i][j]为第 j j j位上,第 a i a_i ai个数之后第一个使得与和等于0的位置。
由于题目要求与起来的和 ≥ k \ge k ≥k,所以对于每一个询问我们把 k k k也拆开来考虑。
这里有一种贪心的思路需要学习:
我们从
k
k
k的二进制高位开始枚举,如果某一位
j
j
j上为0,那么答案就可以更新到std::min(res, nxt[i][j] - 1)
。
如果某一位
j
j
j上为1,我们是不可以把答案直接更新到上面的位置的,因为对于低位来说可能会导致与起来的和无法
≥
k
\ge k
≥k,因此我们需要维护一个res
变量,这个值用来维护期望可以到达的最远位置,用于最后来更新
a
n
s
ans
ans。
#include <bits/stdc++.h>
using i64 = long long;
void solve() {
int n;
std::cin >> n;
std::vector<int> a(n);
std::vector nxt(n + 1, std::vector<int>(30, n));
for (int i = 0; i < n; i++) {
std::cin >> a[i];
}
for (int i = n - 1; i >= 0; i--) {
nxt[i] = nxt[i + 1];
for (int j = 0; j < 30; j++) {
if (~a[i] >> j & 1) {
nxt[i][j] = i;
}
}
}
int q;
std::cin >> q;
while (q--) {
int l, k;
std::cin >> l >> k;
l--;
int ans = l;
int res = n;
for (int i = 29; i >= 0; i--) {
if (k >> i & 1) {
res = std::min(res, nxt[l][i]);
} else {
ans = std::max(ans, std::min(res, nxt[l][i]));
}
}
ans = std::max(ans, res);
if (ans <= l) {
ans = -1;
}
std::cout << ans << " ";
}
std::cout << "\n";
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int t;
std::cin >> t;
while (t--) {
solve();
}
return 0;
}