E. Iva & Pav
链接:https://codeforces.com/problemset/problem/1878/E
洛谷链接:https://www.luogu.com.cn/problem/CF1878E
知识点:st表+二分(我不知道为什么有的题解说不用二分...反正我的在第11个测试点会TLE)
思路就是一样的,存储区间的位与,然后按照区间查询:st_query来看每个区间符不符合,注意,这里的右边界通过二分而来
为什么可以二分:从左端点到右边:连续区间的位与一定是非升数列,结合位与的性质,所以如果小了就移动右边;
注意这里的二分形式可能和之前有点不一样,注意模拟。
代码:
#include<iostream>
#include<vector>
#include<algorithm>
#include<math.h>
#include<sstream>
#include<string>
#include<string.h>
#include<iomanip>
#include<stdlib.h>
#include<map>
#include<queue>
#include<cmath>
#include<limits.h>
#include<climits>
#include<fstream>
#include<stack>
#include<set>
typedef long long ll;
using namespace std;
const int N = 2e5 + 10;
int n;
ll a[N], dp_min[N][21], dp_and[N][21];
int LOG2[N];
void st_init()
{
LOG2[0] = -1;
for (int i = 1; i <= N; i++)LOG2[i] = LOG2[i >> 1] + 1;
for (int i = 1; i <= n; i++)
{
dp_and[i][0] = a[i];
}
int p = LOG2[N];
for (int k = 1; k <= p; k++)
for (int s = 1; s + (1 << k) <= n + 1; s++)
{
dp_and[s][k] = dp_and[s][k - 1] & dp_and[s + (1 << (k - 1))][k - 1];
}
return;
}
int st_query(int l, int kp)
{
if (a[l] < kp)return -1;
else
{
int rr = 0;
int L = l, R = n;//二分
if (L == n - 1)//特判
{
if ((a[n - 1] & a[n]) >= kp)return n;
else return n - 1;
}
while (L <= R)//二分的第二种形式
{
int mid = L + (R - L) / 2; int k = LOG2[mid - l + 1];
int jd = dp_and[l][k] & dp_and[mid - (1 << k) + 1][k];//注意这个jd,就是从左边到当前的mid所有元素的与
if (jd >= kp)L = mid + 1;
else R = mid-1;
}
if (l == n)return l;
return L-1;
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int t; cin >> t;
while (t--)
{
cin >> n;
for (int i = 1; i <= n; i++)cin >> a[i];
int q; cin >> q;
st_init();
for (int i = 0; i < q; i++)
{
int x, k; cin >> x >> k;
cout << st_query(x, k)<< ' ';
}
cout << '\n';
}
return 0;
}