Hossam and Range Minimum Query(主席树 + 随机化哈希 + 强制在线)

https://codeforces.com/contest/1771/problem/F
#include <bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define all(a) (a).begin(), (a).end()
#define IOS ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
using namespace std;
const int N = 2e5 + 10;

/*
题意:求一个区间中出现次数为奇数的最小数(强制在线)

解:主席树上二分
用主席数维护区间前缀异或和
若一个区间的中数的数都出现了偶数次, 则他们的异或和为0
但反过来显然不成立
考虑将数组随机映射到一个很大的值域范围中, 考虑前 n - 1 项的异或和, 因为每一项都是随机的, 
所以异或和也是随机的, 并且第 n 个数也是随机的, 那么不成立的情况就是两个随机的数字(前n-1项异或和, 第n项)相等
概率为 1/值域
*/

int n, a[N], b[N], rk[N], num[N];
int rt[N], tot;
struct node
{
    int ls, rs;
    ull xsum;
} tree[N << 5];

void update(int &x, int y, int l, int r, int pos, ull val)
{
    x = ++tot;
    tree[x] = tree[y];
    tree[x].xsum ^= val;
    if (l == r)
        return;
    int mid = (l + r) / 2;
    if (pos <= mid)
        update(tree[x].ls, tree[y].ls, l, mid, pos, val);
    else
        update(tree[x].rs, tree[y].rs, mid + 1, r, pos, val);
}

int query(int x, int y, int l, int r)
{
    if((tree[x].xsum ^ tree[y].xsum) == 0)              
        return 0;
    if (l == r)
        return num[l];
    int mid = (l + r) / 2;
    if (tree[tree[x].ls].xsum ^ tree[tree[y].ls].xsum)
        return query(tree[x].ls, tree[y].ls, l, mid);
    else
        return query(tree[x].rs, tree[y].rs, mid + 1, r);
}

void solve()
{
    int n;
    cin >> n;
    for (int i = 1; i <= n; i++)
    {
        cin >> a[i];
        b[i] = a[i];
    }

    sort(b + 1, b + 1 + n);
    int len = unique(b + 1, b + 1 + n) - b - 1;

    for (int i = 1; i <= n; i++)
    {
        rk[i] = lower_bound(b + 1, b + 1 + len, a[i]) - b;
        num[rk[i]] = a[i];
    }

    std::mt19937_64 rng(std::chrono::steady_clock::now().time_since_epoch().count());
    vector<ull> h(len + 1);
    for (int i = 1; i <= len; i++)
        h[i] = rng();
                                   
    for (int i = 1; i <= n; i++)
        update(rt[i], rt[i - 1], 1, len, rk[i], h[rk[i]]);

    int q;
    cin >> q;
    int ans = 0;
    while (q--)
    {
        int l, r;
        cin >> l >> r;
        l ^= ans;
        r ^= ans; 
        ans = query(rt[l - 1], rt[r], 1, len);
        cout << ans << '\n';
    }
}

signed main()
{
    IOS;
    int t = 1;
    // cin >> t;
    while (t--)
        solve();
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值