Educational Codeforces Round 46 F. One Occurrence(主席树)

传送门

求任意一个区间中只出现一次的数

区间问题用主席树维护,对前缀建主席树。
主席树需要维护该元素上一次出现的位置,
如果[l,r]中元素上一次出现的位置小于l,则说明有解(并且解就是这个元素)
所以主席树需要维护区间最小值,顺带维护最小值出现的下标。

如果一个元素在区间中出现了很多次:
eg:5,6,5,7,5,8
则元素5上一次出现的位置会不断被后面新出现的5更新。故答案是正确的。

每个版本相较于上一个版本:1.将此位置的值更改为last[i]
2.如果这个数之前出现过,则将last[i]这个位置重新置为INF

#include <bits/stdc++.h>

using namespace std;
//-----pre_def----
const double PI = acos(-1.0);
const int INF = 0x3f3f3f3f;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
#define fir(i, a, b) for (int i = (a); i <= (b); i++)
#define rif(i, a, b) for (int i = (a); i >= (b); i--)
#define endl '\n'
#define init_h memset(h, -1, sizeof h), idx = 0;
#define lowbit(x) x &(-x)

//---------------
const int N = 5e5 + 10;
int n, m, d[N];
int root[N], ls[N * 50], rs[N * 50], minx[N * 50]/*最小值*/, minsub[N * 50]/*最小值出现的下标*/, idx;
int last[N];
//每个节点维护最小值&最小值出现的位置
void pushup(int u)
{
    if (minx[ls[u]] > minx[rs[u]])
    {
        minx[u] = minx[rs[u]];
        minsub[u] = minsub[rs[u]];
    }
    else
    {
        minx[u] = minx[ls[u]];
        minsub[u] = minsub[ls[u]];
    }
}

void build(int &u, int l, int r) //初始化
{
    u = ++idx;
    if (l == r)
    {
        minx[u] = INF;
        minsub[u] = l;
        return;
    }
    int mid = l + r >> 1;
    build(ls[u], l, mid);
    build(rs[u], mid + 1, r);
    pushup(u);
}

void update(int &now, int pre, int l, int r, int x, int k)
{
    now = ++idx;
    ls[now] = ls[pre];
    rs[now] = rs[pre];
    // minx[now] = minx[pre];
    // minsub[now] = minsub[pre];
    if (l == r)
    {
        minx[now] = k;
        minsub[now] = l;
        return;
    }
    int mid = l + r >> 1;
    if (x <= mid)
    {
        update(ls[now], ls[pre], l, mid, x, k);
    }
    else
    {
        update(rs[now], rs[pre], mid + 1, r, x, k);
    }
    pushup(now);
}
PII query(int now, int l, int r, int L, int R)
{
    if (L <= l && r <= R)
    {
        return {minx[now], minsub[now]};
    }
    int mid = l + r >> 1;
    if (R <= mid) //只需要ls就行了
    {
        return query(ls[now], l, mid, L, R);
    }
    else if (mid < L) //只需要rs就行了
    {
        return query(rs[now], mid + 1, r, L, R);
    }
    else
    {
        //返回区间最小值&出现的下标
        PII ll = query(ls[now], l, mid, L, R), rr = query(rs[now], mid + 1, r, L, R);
        if (ll.first <= rr.first)
            return ll;
        else
            return rr;
    }
}
int main()
{
#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
    freopen("out.txt", "w", stdout);
    int StartTime = clock();
#endif
    scanf("%d", &n);
    build(root[0], 1, n);
    fir(i, 1, n)
    {
        scanf("%d", &d[i]);
        //新建版本,修改1~2处
        update(root[i], root[i - 1], 1, n, i, last[d[i]]);
        if (last[d[i]]) //之前出现过
        {
            update(root[i], root[i], 1, n, last[d[i]], INF);
        }
        last[d[i]] = i;
    }
    scanf("%d", &m);
    while (m--)
    {
        int l, r;
        scanf("%d%d", &l, &r);
        PII ans = query(root[r], 1, n, l, r);
        //cout << ans.first << " " << ans.second << endl;
        if (ans.first >= l)
        {
            puts("0");
        }
        else
        {
            printf("%d\n", d[ans.second]);
        }
    }
#ifndef ONLINE_JUDGE
    printf("Run_Time = %d ms\n", clock() - StartTime);
#endif
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值