Closest Equals(离线/线段/思维)

题面:
在这里插入图片描述
题目大意
给出一个序列,每次查询一段区间内距离最近的相同元素之间的距离.
思路:
由于只有两个相同的数在查询区间才会对答案产生贡献
用一个 p r e [ i ] pre[i] pre[i] 来记录和 a [ i ] a[i] a[i]相同的数上一次出现的位置
这两个相同的数产生的贡献就是 w = i − p r e [ i ] w = i - pre[i] w=ipre[i]
我们把这个贡献放在 p r e [ i ] pre[i] pre[i]这个位置上。
那我们要查询[l,r]的答案,这就是区间求最小值问题了
但是我们这里还有一个问题就是:
我们查询 [ l , r ] [l,r] [l,r]的到答案,无法确定这个答案是不是 i i i r r r 后面, p r e [ i ] pre[i] pre[i] [ l , r ] [l,r] [l,r]这种情况产生的贡献。所以我们离线来做,去避免这种情况。
贡献最小值用线段树来维护。
先将询问都存下来,然后从前往后遍历原序列,当能产生贡献时,用线段树更新,当遍历到询问的 r r r ,用线段树查询 [ l , r ] [l,r] [l,r]区间最小值。
代码:

#include <bits/stdc++.h>
#define ll long long
#define ls u<<1
#define rs u<<1|1
using namespace std;

const int N = 5e5 + 5, INF = 0x3f3f3f3f;
int n, m;
int a[N], pre[N];
map<int, int>mp;
int ans[N];

struct node {
    int l, r, v, lazy;
}tr[N * 4];

vector<pair<int, int> >g[N];

void pushup(int u) {
    tr[u].v = min(tr[ls].v, tr[rs].v);
}

void pushdown(int u) {
    if (tr[u].lazy != INF) {
        int v = tr[u].lazy;
        tr[ls].lazy = min(tr[ls].lazy, v);
        tr[rs].lazy = min(tr[rs].lazy, v);

        tr[ls].v = min(tr[ls].v, v);
        tr[rs].v = min(tr[rs].v, v);
        tr[u].lazy = INF;
    }
}

void build(int u, int l, int r) {
    tr[u] = { l,r,INF,INF };
    if (l == r) return;
    int mid = l + r >> 1;
    build(ls, l, mid);
    build(rs, mid + 1, r);
}

void update(int u, int l, int r, int v) {
    if (tr[u].l >= l && tr[u].r <= r) {
        tr[u].v = min(tr[u].v, v);
        tr[u].lazy = min(tr[u].lazy, v);
        return;
    }
    pushdown(u);
    int mid = tr[u].l + tr[u].r >> 1;
    if (l <= mid) update(ls, l, r, v);
    if (r > mid) update(rs, l, r, v);
    pushup(u);
}

int query(int u, int l, int r) {
    if (tr[u].l >= l && tr[u].r <= r) {
        return tr[u].v;
    }
    pushdown(u);
    int mid = tr[u].l + tr[u].r >> 1;
    int r1 = INF, r2 = INF;
    if (l <= mid) r1 = query(ls, l, r);
    if (r > mid) r2 = query(rs, l, r);

    return min(r1, r2);
}


int main() {
    cin >> n >> m;
    for (int i = 1; i <= n; i++) cin >> a[i];

    for (int i = 1; i <= n; i++) {
        if (!mp.count(a[i])) {
            pre[i] = i;
            mp[a[i]] = i;
        }
        else {
            pre[i] = mp[a[i]];
            mp[a[i]] = i;
        }
    }

    for (int i = 1; i <= m; i++) {
        int l, r;
        cin >> l >> r;
        g[r].push_back({ l, i });
    }

    build(1, 1, n);

    for (int i = 1; i <= n; i++) {
        int v = i - pre[i];
        if (v > 0) {
            update(1, pre[i], pre[i], v);
        }
        for (auto [l, id] : g[i]) {
            int res = query(1, l, i);
            ans[id] = res == INF ? -1 : res;
        }
    }
    for (int i = 1; i <= m; i++) cout << ans[i] << "\n";
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值