宝石专家

题目描述

Jim是一位宝石收藏品行家,在他的收藏室里保存着许多珍贵的宝石,磷叶石、钻石、摩根石、透绿柱石….,已知Jim有n个宝石,现在他将这n个宝石从1到n排开编号从1到n。Jim发现他所有的宝石中竟然有不少是完全相同的的,我们规定每个宝石都有一个特征值ai,当两个宝石特征值相等时及认为两个宝石相同。Jim发现两个相同的宝石离得越接近越明显。Jim现在有m个问题,他想问你在.编号l到r这一区间里的所有宝石中,两个相同宝石的最近距离是多少,(两个宝石的距离是它们编号的绝对值之差)。

保证 l < r ,对于 ax和ay 若ax=ay 它们的距离为|x-y|。

题意

就是询问有许多线段,每一个线段有权值,询问在区间[l,r]中线段的最小权值。

solution

把每一个线段与询问离线并按左排序,用2-pointer从大向小扫,保证对于询问时所有的线段是合法的。

然后使用线段树,询问 [0,r] 之间的最小值即可。

#include<bits/stdc++.h>
#define L first
#define R second
using namespace std;
int n;
int a[200100];
struct sgt{
    int val;
    int l, r;
}d[200100 << 2];
pair <int,int> p[200100]; int pl;
map <int,int> t;
int ans[200100];
int q;
struct qry{
    int id;
    int l, r;
    bool operator < (qry t){
        return l < t.l;
    }
}st[200100];
void build(int x,int l,int r){
    d[x].l = l, d[x].r = r;
    d[x].val = 0x3f3f3f3f;
    if (l == r) return;
    build(x<<1,l,(l+r)/2);
    build(x<<1|1,(l+r)/2+1,r);
}
void insert(int x,int p,int val){
    d[x].val = min(val, d[x].val);
    if (d[x].l == d[x].r) return;
    if (p <= d[x<<1].r) insert(x<<1,p,val);
    else insert(x<<1|1,p,val);
}
int query(int x,int p){
    if (d[x].l == d[x].r) return d[x].val;
    if (p <= d[x<<1].r) return query(x<<1,p);
    else return min(d[x<<1].val, query(x<<1|1,p));
}
int main(){
    cin >> n >> q;
    for (int i=1;i<=n;i++){
        scanf("%d", a+i);
        if (t.count(a[i])) p[++pl].L = t[a[i]], p[pl].R = i, t[a[i]] = i;
        else t[a[i]] = i;
    }
    for (int i=1;i<=q;i++){
        st[i].id = i;
        scanf("%d%d",&st[i].l,&st[i].r);
    }
    sort(p+1,p+pl+1);
    sort(st+1,st+q+1);
    int pl1 = pl;
    int pl2 = q;
    build(1,1,n);
    while (pl2 > 0){
        while (p[pl1].L >= st[pl2].l) insert(1,p[pl1].R,p[pl1].R-p[pl1].L), pl1--;
        ans[st[pl2].id] = query(1,st[pl2].r);
        pl2 --;
    }
    for (int i=1;i<=q;i++){
        printf("%d\n",(ans[i]==0x3f3f3f3f?-1:ans[i]));
    }
}
12-29 18:52