原题链接:F. One Occurrence
题目大意:
给出一个长度为 n n n 的数组 a a a。
现在给出 q q q 次询问,每次询问一个数组区间 [ l , r ] [l, r] [l,r] (即 a l , a l + 1 , . . . , a r a_l,a_{l+1},...,a_{r} al,al+1,...,ar),如果区间内存在某个数字 a i a_i ai 只出现了一次,输出这个数字(有多个输出任意一个均可),否则输出 0 0 0 。
解题思路:
我们可以这么想, a i a_{i} ai 出现两次及以上当且仅当 a j = a i ( j < i ) a_{j} = a_{i}(j<i) aj=ai(j<i) ,即一个区间 [ l , r ] [l, r] [l,r] 同时包含 i , j i,j i,j 时满足。
维护某个数字出现次数超过两次以上不好维护,那么我们利用扫描线的思想或主席树的思想即可。
以下标为时间轴维护一个前缀的信息:
假设当前枚举到第 i i i 个下标,维护 a i a_{i} ai 的上一次出现位置 j j j 在什么地方。
显然这样,只要我们的询问的区间 [ l , i ] [l, i] [l,i] 包含 j j j ,即 l ≤ j < i l \leq j < i l≤j<i 则说明答案不合法。
反过来说,想要让答案合法,我们只需要知道是否存在某个 a j a_j aj 满足 j < l j < l j<l 即可。
那么做法显而易见,我们对每个 a i a_i ai 维护上一次出现的下标 j j j 即可。
具体而言,按照时间轴构建主席树,当前枚举到 i i i 时候,将上一次 a i a_i ai 出现的位置 j j j 在主席树中改为正无穷,下标 i i i 修改为 j j j 即可。
查询 [ l , r ] [l, r] [l,r] 时,我们在第 r r r 个版本的主席树上查询 [ l , r ] [l, r] [l,r] 内 j j j 的最小值,假设 j < l j < l j<l 直接输出下标 j j j 对应的值,否则输出 0 0 0,注意一些细节即可。
时间复杂度 O ( n log n ) O(n \log n) O(nlogn)
#include <bits/stdc++.h>
using namespace std;
using PII = pair<int, int>;
using i64 = long long;
const int N = 1e6 + 1;
#define lson(x) Seg[x].l, l, mid
#define rson(x) Seg[x].r, mid + 1, r
#define val(x) Seg[x].val
#define ls(x) Seg[x].l
#define rs(x) Seg[x].r
struct SegTree {
int l, r;
int p, val;
} Seg[N * 40];
int idx = 0;
SegTree merge(SegTree& P, const SegTree A, const SegTree B) {
P.p = (A.val <= B.val ? A.p : B.p);
P.val = min(A.val, B.val);
return P;
}
int creat(int& pre) {
Seg[++idx] = Seg[pre];
return idx;
}
int build(int &pre, int l, int r) {
int cur = creat(pre);
val(cur) = 1e9;
if (l == r) {
Seg[cur].p = l;
return cur;
}
int mid = l + r >> 1;
ls(cur) = build(lson(pre));
rs(cur) = build(rson(pre));
Seg[cur] = merge(Seg[cur], Seg[ls(cur)], Seg[rs(cur)]);
return cur;
}
int Modify(int &pre, int l, int r, int p, int v) {
int cur = creat(pre);
if (l == r) {
val(cur) = v;
return cur;
}
int mid = l + r >> 1;
if (p <= mid) {
ls(cur) = Modify(lson(pre), p, v);
} else {
rs(cur) = Modify(rson(pre), p, v);
}
Seg[cur] = merge(Seg[cur], Seg[ls(cur)], Seg[rs(cur)]);
return cur;
}
SegTree Query(int &k, int l, int r, int x, int y) {
if (l >= x && r <= y) return Seg[k];
int mid = l + r >> 1; SegTree res{};
if (x > mid) return Query(rson(k), x, y);
if (y <= mid) return Query(lson(k), x, y);
return merge(res, Query(lson(k), x, y), Query(rson(k), x, y));
}
void solve() {
int n;
cin >> n;
vector<int> Last(5e5 + 1), ver(n + 1);
ver[0] = build(ver[0], 1, n);
vector<int> a(n + 1);
for (int i = 1; i <= n; ++i) {
cin >> a[i];
if (Last[a[i]]) {
ver[i] = Modify(ver[i - 1], 1, n, Last[a[i]], 1e9);
ver[i] = Modify(ver[i], 1, n, i, Last[a[i]]);
} else {
ver[i] = Modify(ver[i - 1], 1, n, i, Last[a[i]]);
}
Last[a[i]] = i;
}
int q;
cin >> q;
for (int i = 1; i <= q; ++i) {
int l, r;
cin >> l >> r;
SegTree T = Query(ver[r], 1, n, l, r);
if (T.val < l) {
cout << a[T.p] << '\n';
} else {
cout << 0 << '\n';
}
}
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int t = 1; //cin >> t;
while (t--) solve();
return 0;
}