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;
}
posted @ 2026-04-16 17:05  lucasincyber  阅读(3)  评论(0)    收藏  举报