算法分析
ST表 + 思维 ??(只要是能维护区间的数据结构就可以
每次就是查询一段区间内相同的数之间最短的距离
那么直接一点去思考
我们可以将每一对这样的数存下来,然后找最小的
那么这样子显然是会有很多对是重叠的比如说 1 1 1 1 1
肯定要选择第一个和第二个 1 做匹配才是最短的
因此我们以左端点为起始点,只要右端点大于之前已经匹配的位置的对,一定不是最短的
那么就可以除去很多对了,左端点不同的则是新的一对区间,保证了左端点有序
我们用 维护第 对区间的端点,
然后经典 ST 表 维护 从 开始长度为 长度的区间最小值就可以了
询问的时候,我们只需要在 中二分找到在这个区间内第一个左端点大于等于 范围的和最后一个右端点 小于等于 的,在这区间内询问答案即可。
AC Code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 5e5 + 10;
map<int,int> mp;
int f[N][21],l[N],r[N],Log[N],cnt;
void init(int n)
{
Log[0] = - 1;
for(int i = 1;i <= n;i ++)
{
Log[i] = Log[i >> 1] + 1;
f[i][0] = r[i] - l[i];
}
for(int j = 1;j <= Log[n];j ++)
for(int i = 1;i + (1 << j) - 1 <= n;i ++)
f[i][j] = min(f[i][j - 1],f[i + (1ll << (j - 1))][j - 1]);
}
int query(int x,int y)
{
int k = Log[y - x + 1];
return min(f[x][k],f[y - (1 << k) + 1][k]);
}
int main()
{
int n,m;
scanf("%d%d",&n,&m);
for(int i = 1;i <= n;i ++)
{
int x;scanf("%d",&x);
if(mp[x])
{
if(mp[x] > l[cnt])
{
l[++ cnt] = mp[x];
r[cnt] = i;
}
}
mp[x] = i;
}
init(cnt);
while(m --)
{
int L,R;
scanf("%d%d",&L,&R);
int x = lower_bound(l + 1,l + 1 + cnt,L) - l;
int y = upper_bound(r + 1,r + 1 + cnt,R) - r - 1;
if(x <= y) printf("%d\n",query(x,y));
else puts("-1");
}
}