Solution
- pre[i] 记录 前面等于a[i]且离a[i]最近的值的下标。
- 将查询存起来,按右端点从小到大排序。
- 从1~n遍历,若pre[i]不为0, 则将
i - pre[i]
存入下标pre[i]
的点
遍历过程中,若i 等于某个查询的右端点,直接用线段树查询区间的最小值即可
Code
本题的主要思想就是:
- 将最近的两个点对答案的贡献,转化为这两个点中左边这个点的贡献。
- 离线查询的过程中,始终保证线段树修改的点的右端点小于等于当前查询的点。
using namespace std;
const int inf = 0x3f3f3f3f;
const int N = 5e5 + 5, M = 5e6 +7;
int a[N];
struct node
{
int x, y, id;
bool operator < (node a) const
{
return y < a.y;
}
}s[N];
int ans[N];
struct seg
{
int l, r;
int dat;
#define tp t[p]
#define tl t[p << 1]
#define tr t[p << 1 | 1]
}t[N * 4];
void build(int p, int l, int r)
{
tp.l = l; tp.r = r;
if (l == r) {
tp.dat = inf; return;
}
int mid = l + r >> 1;
build(p << 1, l, mid);
build(p << 1 | 1, mid + 1, r);
tp.dat = min(tl.dat, tr.dat);
}
void change(int p, int x, int y)
{
if (tp.l == tp.r) { tp.dat = y; return; }
int mid = tp.l + tp.r >> 1;
if (x <= mid) change(p << 1, x, y);
else change(p << 1 | 1, x, y);
tp.dat = min(tl.dat, tr.dat);
}
int ask(int p, int l, int r)
{
if (l <= tp.l && r >= tp.r) return tp.dat;
int mid = tp.l + tp.r >> 1;
int ans = inf;
if (l <= mid) ans = min(ans, ask(p << 1, l, r));
if (r > mid) ans = min(ans, ask(p << 1 | 1, l, r));
return ans;
}
map<int, int>mp;
int pre[N];
int main()
{
IOS;
int n, m; cin >> n >> m;
for (int i = 1; i <= n; i++) cin >> a[i];
for (int i = 1; i <= m; i++)
{
cin >> s[i].x >> s[i].y;
s[i].id = i;
}
sort(s + 1, s + m + 1);
build(1, 1, n);
for (int i = 1; i <= n; i++)
{
if (mp[a[i]])
{
pre[i] = mp[a[i]];
mp[a[i]] = i;
}
else mp[a[i]] = i;
}
for (int i = 1, r = 1; i <= n; i++)
{
if (pre[i]) change(1, pre[i], i - pre[i]);
while (r <= m && i == s[r].y)
{
ans[s[r].id] = ask(1, s[r].x, s[r].y);
r++;
}
}
for (int i = 1; i <= m; i++)
cout << (ans[i] == inf ? -1 : ans[i]) << "\n";
return 0;
}