CodeForces - 522D

描述:
You are given sequence a1, a2, …, an and m queries lj, rj (1 ≤ lj ≤ rj ≤ n). For each query you need to print the minimum distance between such pair of elements ax and ay (x ≠ y), that:

both indexes of the elements lie within range [lj, rj], that is, lj ≤ x, y ≤ rj;
the values of the elements are equal, that is ax = ay.
The text above understands distance as |x - y|.

Input
The first line of the input contains a pair of integers n, m (1 ≤ n, m ≤ 5·105) — the length of the sequence and the number of queries, correspondingly.

The second line contains the sequence of integers a1, a2, …, an ( - 109 ≤ ai ≤ 109).

Next m lines contain the queries, one per line. Each query is given by a pair of numbers lj, rj (1 ≤ lj ≤ rj ≤ n) — the indexes of the query range limits.

Output
Print m integers — the answers to each query. If there is no valid match for some query, please print -1 as an answer to this query.

题目大意:
给n个数a[1],a[2],a[3]……a[n],和q次询问区间[ l , r ],询问区间内是否存在a[i],使得next[a[i]] ≤ r,若存在输出 最小的(next[a[i]] - i ) ,不存在输出-1
next[a[i]]表示a[i]下一次出现在数组中位置
思路: 线段树+离线处理
先想清楚线段树维护什么?
想法一:维护区间某个数到下一个数的最小位置(受之前类似题的影响https://ac.nowcoder.com/acm/contest/9983/E可以看看
存在性很好判,区间最小位置是否 ≤ 询问区间的右界,但是求最小next[a[i]] - i 不好搞。
正解:线段树+离线处理
维护存在相同数字区间的最小区间距 r - l
将询问区间按照右端点从小到大排序,将存在相同数的区间也按照右端点从小到大排序,每次处理询问区间时,将右端点小于询问区间右端点存在相同数区间插到树上,(之所以按照右端点排序是满足无后效性),求下一个询问区间的树是建立在上一个区间更新的树基础上。
这样一来,转化为已知位置和权值求最小值,容易很多。
ps:注意不要size()-1的操作,空容器时会出现奇怪的错误
不用建空树了,初始化时每个区间赋值为正无穷。

#include <bits/stdc++.h>

using namespace std;
#define IOS ios::sync_with_stdio(false);cin.tie(0);
#define _for(i,a,b) for(int i=(a) ;i<=(b) ;i++)
#define _rep(i,a,b) for(int i=(a) ;i>=(b) ;i--)
#define pb(v) push_back(v)
#define int long long
typedef long long ll;
const int N = 5e5+5;
int n,q;
struct node
{
    
    
    //对询问区间v是下标,对于插入区间v是区间距r - l
    int l,r,v;//左右端点   和   间隔/下标
    node(int l,int r,int v):l(l),r(r),v(v){
    
    }
};
int a[N];
int tree[N*4];
map <int ,int> mp;//处理区间,+-1e9用数组会爆内存 = =
vector <node > que;//询问
vector <node > lis;//待插入区间
int ans[N];
bool cmp(node a, node  b )
{
    
    
    return a.r < b.r;
}
void pushup(int p, int l, int r)
{
    
    
    tree[p] = min( tree[2*p],tree[2*p+1]);
}
void update(int p,int l,int r ,int x,int v)//每次插入更新树
{
    
    
    if( l == r)
    {
    
    
        tree[p] = v;
        return ;
    }
    int mid = (l+r)>>1;
    if( x<=mid)
    {
    
    
        update(2*p,l,mid,x,v);
    }
    else
    {
    
    
        update(2*p+1 , mid+1 , r, x,v);
    }
    pushup(p,l,r);
}
int find(int p ,int l, int r, int a, int b)
{
    
    
    if( a<=l && r<=b )
    {
    
    
        return tree[p];
    }
    int mid=(l+r)>>1;
    int res= 0x3f3f3f3f;
    if( a<=mid ) res = min( res, find(2*p,l,mid,a,b));
    if( b>=mid+1 ) res = min( res, find(2*p+1,mid+1,r,a,b));
    return res;
}
signed main()
{
    
    
//    freopen("data.txt","r",stdin);
    IOS;
    cin>>n>>q;
    _for(i,0,4*n)
    {
    
    
        tree[i] = 0x3f3f3f3f;
    }
    _for(i,1,n)
    {
    
    
        cin>>a[i];
        if( mp[a[i]] )
        {
    
    
            lis.pb(node(mp[a[i]] ,i ,i-mp[a[i]] ));
        }
        mp[a[i]] = i;
    }
    _for(i,1,q)
    {
    
    
        int l,r;
        cin>>l>>r;
        que.pb(node(l,r,i));
    }
    //对区间排序
    sort(que.begin() , que.end() , cmp);
    sort(lis.begin() , lis.end() , cmp);

    int j=0;
    _for( i , 0 , que.size()-1 )
    {
    
    
        while(j<lis.size() && lis[j].r <= que[i].r )
        {
    
    
            update(1,1,n,lis[j].l,lis[j].v);
            j++;
        }

        int temp = find(1,1,n,que[i].l,que[i].r);
        //按照原来输入的顺序存好答案
        ans[que[i].v] = (temp >= 0x3f3f3f3f? -1:temp);
    }
    _for(i,1,q)
    {
    
    
        cout<<ans[i]<<endl;
    }
}

猜你喜欢

转载自blog.csdn.net/m0_53688600/article/details/115282830