题目描述
难度分:1400
输入T(≤104)表示T组数据。所有数据的n之和≤2×105,q之和≤2×105。
每组数据输入n(1≤n≤2×105)和长为n的数组a(1≤a[i]≤109),下标从1开始。
然后输入q(1≤q≤105)和q个询问,每个询问输入L(1≤L≤n)和k(1≤k≤109)。
对于每个询问,输出最大的R,满足非空连续子数组a[L]~a[R]的AND
≥k。
如果不存在这样的R,输出−1。
输入样例
3
5
15 14 17 42 34
3
1 7
2 15
4 5
5
7 5 3 1 7
4
1 7
5 7
2 3
2 2
7
19 20 15 12 21 7 11
4
1 15
4 4
7 12
5 7
输出样例
2 -1 5
1 5 2 2
2 6 -1 5
算法
ST表+二分答案
比较容易注意到按位与运算是具有单调性的,越与只可能越小或者不变。因此一旦a[L]<k肯定就无解了,输出−1。一般的情况就二分来确定最右的R,如果a[L…R]的AND
≥k,那么更小的R肯定也是满足的,可以往右搜索更大的R。
而在二分的过程中,我们需要快速求得子数组的按位与结果,可以预处理出一个按位与的ST表,这样就可以O(1)查询任意子数组的按位与结果。
复杂度分析
时间复杂度
预处理出ST表的时间复杂度为O(nlog2n)。每个询问需要二分得到R,时间复杂度为O(log2n),q个询问的时间复杂度为O(qlog2n)。整个算法的时间复杂度为O((n+q)log2n)。
空间复杂度
ST表是整个算法的空间瓶颈,因此额外空间复杂度为O(nlog2n)。
C++ 代码
#include <bits/stdc++.h>
using namespace std;
const int N = 200010, M = 20;
int T, n, q, a[N], st[N][M];
int query(int l, int r) {
int k = log2(r - l + 1);
return st[l][k] & st[r - (1<<k) + 1][k];
}
int main() {
scanf("%d", &T);
while(T--) {
scanf("%d", &n);
for(int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
}
for(int j = 0; j < M; j++) {
for(int i = 1; i + (1 << j) - 1 <= n; i++) {
if(!j) {
st[i][j] = a[i];
}else {
st[i][j] = st[i][j - 1] & st[i + (1<<(j - 1))][j - 1];
}
}
}
scanf("%d", &q);
while(q--) {
int l, k;
scanf("%d%d", &l, &k);
if(a[l] < k) {
printf("%d ", -1);
}else {
int left = l, right = n;
while(left < right) {
int mid = left + right + 1 >> 1;
if(query(l, mid) >= k) {
left = mid;
}else {
right = mid - 1;
}
}
printf("%d ", right);
}
}
puts("");
}
return 0;
}