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

posted on 2024-05-05 14:07  WHUStar  阅读(2)  评论(0编辑  收藏  举报