abc 250 e 题解
abc 250 e
题意
给定两个长度为 \(n\) 的序列 \(a, b\),有 \(q\) 次询问,每次询问给出 \(x_i, y_i\),请你回答 \(a\) 的前 \(x_i\) 项所构成的集合与 \(b\) 的前 \(y_i\) 项所构成的集合是否相同。
思路
暴力
每次枚举 \(a\) 的前 \(x_i\) 个元素和 \(b\) 的前 \(y_i\) 个元素,用两个 set
维护集合,最后比较即可。
时间复杂度为 \(O(n ^ 2 \times \log n)\)。
正解
我们可以将先预处理出 \(n\) 个区间:对于 \(a\) 的前 \(i\) 项构成的集合,在第 \(i\) 个区间内的所有 \(j\),\(b\) 的前 \(j\) 项所构成的集合都和 \(a\) 的前 \(i\) 项构成的集合相同。
那么,每次询问只需要判断 \(y_i\) 是否在第 \(x_i\) 个区间内就可以了。
但是,我们应该如何处理出这些区间呢?
可以直接双指针。
令 \(a\) 序列中的某个前缀所构成的集合为 \(S1\),\(b\) 序列中的某个前缀所构成的集合为 \(S2\)。
很明显,每次遍历到一个 \(i\),都需要将 \(a_i\) 加入 \(S1\),而这个操作,可能会改变 \(S1\),就将 \(b\) 的指针往后挪,并且将 \(b_j\) 放进 \(S2\),前提是,\(b_j\) 在 \(S1\) 中。当 \(S1\) 的大小与 \(S2\) 的大小相等时,便可以更新区间了。
但是,如果没有改变 \(S1\),便与第 \(i - 1\) 个区间是一样的。
时间复杂度为 \(O(n \times \log n)\)。
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
int n, q, a[N], b[N], l[N], r[N];
set<int> s1, s2;
int main() {
ios::sync_with_stdio(0), cin.tie(0);
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
for (int i = 1; i <= n; i++) {
cin >> b[i];
}
for (int i = 1, j = 1; i <= n; i++) {
if (s1.find(a[i]) != s1.end() && s1.size() == s2.size()) { // 没有改变 s1
l[i] = l[i - 1], r[i] = r[i - 1];
}
s1.insert(a[i]);
for (; j <= n && s1.find(b[j]) != s1.end(); j++) {
s2.insert(b[j]);
if (s1.size() == s2.size()) { // 更新区间
if (!l[i]) {
l[i] = j;
}
r[i] = j;
}
}
}
cin >> q;
while (q--) {
int x, y;
cin >> x >> y;
cout << (l[x] <= y && y <= r[x] ? "Yes\n" : "No\n");
}
return 0;
}