F 小苯的回文询问

题意

给定一个长为 n 的数组,给你 q 次询问,每次询问数组区间 [l, r] 中是否存在一个子序列满足其是一个长度大于 2 的回文子序列

思路

可以发现我们只需要考虑回文长度为 3 的回文子序列即可,因为大于 3 的回文子序列里面都包含着长度为 3 的子序列

我们对每一个数字都记下其上一次出现的位置的下标为 la[i],如果没有则记为 0

因为题目要求回文子序列的长度至少为 3,所以记录的 la[i] 还存在 la[i] = i - 1 的情况。那么可以从后往前遍历整个 la 数组,令 la[i] = i - 1 的情况变为 la[i] = la[la[i]]。如此,la 数组的含义为:使得下标为 i 的数成为回文子序列的一部分的时候,左边界的位置。

那么怎么处理询问呢?相当于我们要在给定区间 [l, r] 中判断是否存在一个下标 i,满足 la[i] >= l && i <= r。那么是不是考虑 [l, r] 区间数组 la 的最大值即可?那么问题不久转化为了询问区间最大值吗?线段树等区间查询工具可以解决该问题。

另一种思路,我们对数组 la 取前缀 max,判断区间 [l, r] 是否满足条件即判断 la[r] >= l 即可。因为数组 la 存在一个前提条件:la[i] < i,所以,如果说满足 max{la[1 ~ r]} >= l,即意味着存在下标 i,满足 la[i] >= l && i >= l && i <= r,符合题意。所以也可以 解决这个问题。

代码

//Author: Qiansui
//Time: 2024-03-27 11:56
#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define mem(x,y) memset(x, y, sizeof(x))
#define debug(x) cout << #x << " = " << x << '\n'
#define debug2(x,y) cout << #x << " = " << x << " " << #y << " = "<< y << '\n'
//#define int long long

using namespace std;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef pair<ull, ull> pull;
typedef pair<double, double> pdd;
/*

*/
const int N = 2e5 + 5, inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f, mod = 998244353;

void solve(){
	int n, q;
	cin >> n >> q;
	vector<int> a(n + 1), la(n + 1);
	map<int, int> pos;
	for(int i = 1; i <= n; ++ i){
		cin >> a[i];
		la[i] = pos[a[i]];
		pos[a[i]] = i;
	}
	for(int i = n; i >= 1; -- i){
		if(la[i] == i - 1)
			la[i] = la[la[i]];
	}
	for(int i = 1; i <= n; ++ i)
		la[i] = max(la[i], la[i - 1]);
	while(q --){
		int l, r;
		cin >> l >> r;
		if(la[r] >= l) cout << "YES\n";
		else cout << "NO\n";
	}
	return ;
}

signed main(){
	// freopen("in.txt", "r", stdin);
	// freopen("out.txt", "w", stdout);
	ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
	int _ = 1;
	// cin >> _;
	while(_ --){
		solve();
	}
	return 0;
}