描述:
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;
}
}