D.找更多的数字(题解)
前言
看一眼题目就想到了之前做过的cf上面一道主席树的题,码了半天才过,最后赛后一看过的人这么多,疑惑不解。看了眼题解区,原来有这么多简单做法,有利用数学方法解方程的,有利用异或结论,搞个前缀和直接秒的,这里就分享一下我的麻烦做法。
题意
给一个数组,多次询问,每次询问一个区间,保证区间内恰好有2个数只出现1次,其他数出现2次,输出只出现一次的这两个数。
分析
cf上面有个题,是判断区间恰好有一个数出现奇数次,其他是偶数次的,算是这题的进阶版本,直接沿用那题的思路,核心还是利用出现2次异或和为0.
对每个数随机一个大的权值,防止哈希碰撞。然后建一棵主席树,维护值域上的异或前缀和,利用主席树,就可以每次得到一个区间内,对应的每个值域的哈希异或和。然后在主席树上二分,如果左子树的异或和是0,说明值域在 内的数都是恰好出现2次。否则说明值域在 内的数至少有一个出现1次,则递归进去查找,右子树同理,递归到叶子则说明找到了,返回,一次查询恰好会有两个地方递归到叶子,单次查询的复杂度是 , 总的复杂度是 ,不过由于主席树的常数较大,实际跑起来甚至不如排行榜里面有个人用 set 模拟主席树乱搞来的快。
代码
#include <bits/stdc++.h>
#define int long long
#define inf 0x3f3f3f3f
#define ll long long
#define pii pair<int, int>
#define tii tuple<int, int, int>
#define db double
#define all(a) a.begin(), a.end()
using namespace std;
const int maxn = 1e6 + 10;
const int mod = 998244353;
mt19937_64 rng(chrono::steady_clock::now().time_since_epoch().count());
int val[maxn], root[maxn];
vector<int> vec;
struct SegmentTree {
#define lson t[rt].ls
#define rson t[rt].rs
#define mid ((l + r) >> 1)
struct Node {
int ls, rs, sum;
};
int tot;
vector<Node> t;
SegmentTree(int n) { t.resize(n << 5); }
void push_up(int rt) { t[rt].sum = t[lson].sum ^ t[rson].sum; }
void modify(int& rt, int pre, int l, int r, int pos) {
rt = ++tot;
t[rt] = t[pre];
if (l == r) {
t[rt].sum ^= val[l];
return;
}
if (pos <= mid)
modify(lson, t[pre].ls, l, mid, pos);
else
modify(rson, t[pre].rs, mid + 1, r, pos);
push_up(rt);
}
void query(int now, int pre, int l, int r) {
if (l == r) {
if (t[now].sum ^ t[pre].sum)
vec.push_back(l);
return;
}
if (t[t[now].ls].sum ^ t[t[pre].ls].sum)
query(t[now].ls, t[pre].ls, l, mid);
if (t[t[now].rs].sum ^ t[t[pre].rs].sum)
query(t[now].rs, t[pre].rs, mid + 1, r);
}
};
void solve() {
for (int i = 0; i <= 1e6; i++)
val[i] = rng();
int n, q;
cin >> n >> q;
SegmentTree seg(n);
for (int i = 1; i <= n; i++) {
int x;
cin >> x;
seg.modify(root[i], root[i - 1], 0, 1e6, x);
}
while (q--) {
int l, r;
cin >> l >> r;
vec.clear();
seg.query(root[r], root[l - 1], 0, 1e6);
if (vec[0] > vec[1])
swap(vec[0], vec[1]);
assert(vec.size() == 2);
cout << vec[0] << " " << vec[1] << "\n";
}
}
signed main() {
// freopen("1.in", "r", stdin);
// freopen("1.out", "w", stdout);
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int t = 1;
// cin >> t;
while (t--)
solve();
return 0;
}