Loading

abc 250 e 题解

abc 250 e

AT 链接

洛谷链接

题意

给定两个长度为 \(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;
}
posted @ 2024-09-20 17:07  chengning0909  阅读(5)  评论(0编辑  收藏  举报