CF1771F Hossam and Range Minimum Query 题解
思路
首先看到了奇数次的数。想到异或哈希。
::::info[异或哈希是啥?]
我们需要快速判断一个数出现了偶数次还是奇数次。我们会发现异或(\(\oplus\))有一个非常奇妙的性质:
\[0 \oplus a = a, a \oplus a = 0
\]
所以我们可以用哈希快速判断出现次数的奇偶性:若异或和为 \(0\),则出现次数为偶数(两两抵消),否则出现次数为奇数(两两抵消后还剩下一个)。
::::
这道题是典型的静态区间的询问,考虑用主席树维护一个前缀的异或和。接下来就很好做了。先离散化一下,套一个值域主席树板子,并且在主席树上二分。若当前左儿子的值 \(> 0\),则答案一定在左儿子,否则就在右儿子。
注意:求随机数时需要用 mt19937。
代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 2e5 + 5;
mt19937 rng(time(0));
int n, q, cur;
int a[N], b[N], val[N], root[N];
struct per_seg_tree
{
struct node
{
int ls, rs, sum;
} tr[N << 5];
void push_up(int p)
{
tr[p].sum = tr[tr[p].ls].sum ^ tr[tr[p].rs].sum;
}
int modify(int rt, int l, int r, int k)
{
int p = ++cur;
tr[p] = tr[rt];
if (l == r)
{
tr[p].sum ^= val[k];
return p;
}
int mid = (l + r) >> 1;
if (k <= mid) tr[p].ls = modify(tr[p].ls, l, mid, k);
else tr[p].rs = modify(tr[p].rs, mid + 1, r, k);
push_up(p);
return p;
}
int query(int rtl, int rtr, int l, int r)
{
if ((tr[rtl].sum ^ tr[rtr].sum) == 0) return 0;
if (l == r) return l;
int mid = (l + r) >> 1, now = tr[tr[rtr].ls].sum ^ tr[tr[rtl].ls].sum;
if (now != 0) return query(tr[rtl].ls, tr[rtr].ls, l, mid);
return query(tr[rtl].rs, tr[rtr].rs, mid + 1, r);
}
} PST;
signed main()
{
scanf("%lld", &n);
for (int i = 1; i <= n; i++)
scanf("%lld", &a[i]), b[i] = a[i];
sort(b + 1, b + n + 1);
int len = unique(b + 1, b + n + 1) - b - 1;
for (int i = 1; i <= len; i++)
val[i] = rng();
for (int i = 1; i <= n; i++)
{
int now = lower_bound(b + 1, b + len + 1, a[i]) - b;
root[i] = PST.modify(root[i - 1], 1, len, now);
}
scanf("%lld", &q);
int lst = 0;
while (q--)
{
int l, r;
scanf("%lld%lld", &l, &r);
l = l ^ lst, r = r ^ lst;
int res = PST.query(root[l - 1], root[r], 1, len);
lst = (res ? b[res] : 0);
printf("%lld\n", lst);
}
return 0;
}

浙公网安备 33010602011771号