Closest Equals
Closest Equals
题目大意
给定一个下标从 \(1 ~ n\) 的序列 \(a\),然后进行 \(m\) 次询问。
每次询问给出一对 \([l,r]\),找到区间中数值相等的且距离最相近的两个数 ai 和 aj,求它们的距离。
换言之找到一组数 \((a_i,a_j)\) 满足
-
\(a_i=a_j\);
-
\(l≤i,j≤r (i≠j)\);
求 \(|i−j|\) 的最小值,如果区间中不存在相等的两个数,则输出 \(−1\)。
思路
首先对于一个数\(x\)来说,如果他出现的位置为 \(x, y, z,(x < y < z)\),那么对答案造成贡献的就只有 \((x,y)\)和 \((y, z)\)两个区间。
接着对于\((x, y)\)来说,其对所有的满足 \(l \leq x, y \leq r\) 的 \(l ,r\)都会造成贡献。
将\((x,y)\)与\((l,r)\)表示在二维坐标系上为(图有点丑陋):
以\(l\)为横坐标,\(r\)为纵坐标,在每个点\((x,y)\)与\((1, n)\)所围成的区域中的每个点是可以取到答案\(y - x\)的区间\((l,r)\)。
并且可以看出\(l\)小的区域是可以取到\(l\)大的区域的答案的,因此我们将每个点\((x, y)\)和每个询问\((l, r)\)预处理出来,并且以\(l\)从大到小排序后遍历。
对于某个\(l\),首先将保存在\(l\)出的点\((x, y)\) 进行处理,我们只需要在纵坐标上维护一个区间最小值的线段树,每次用\(y - x\)更新\((y, n)\)这个区间的值,更新完毕后,再处理这个\(l\)上保存的询问即可,每次询问都直接单点询问答案即可。
Code
#include <bits/stdc++.h>
using namespace std;
const int N = 5e5+7;
struct ${
int l,r,mid,lz,mi;
}tree[N << 2];
void build(int l,int r,int rt = 1) {
int mid = (l + r) >> 1;
tree[rt].mid = mid;
tree[rt].l = l;
tree[rt].r = r;
tree[rt].lz = tree[rt].mi = 1e9;
if (l == r)
return;
build(l, mid, rt << 1);
build(mid + 1, r, rt << 1 | 1);
}
void pushup(int rt) {
tree[rt].mi = min(tree[rt << 1].mi, tree[rt << 1 | 1].mi);
}
void pushdown(int rt) {
if (tree[rt].lz != 1e9) {
tree[rt << 1].lz = min(tree[rt << 1].lz, tree[rt].lz);
tree[rt << 1].mi = min(tree[rt << 1].mi, tree[rt << 1].lz);
tree[rt << 1 | 1].lz = min(tree[rt << 1 | 1].lz, tree[rt].lz);
tree[rt << 1 | 1].mi = min(tree[rt << 1 | 1].mi, tree[rt << 1 | 1].lz);
tree[rt].lz = 1e9;
}
}
void change(int l,int r,int x,int rt) {
if (l <= tree[rt].l && tree[rt].r <= r) {
tree[rt].mi = min(tree[rt].mi, x);
tree[rt].lz = min(tree[rt].lz, x);
return;
}
pushdown(rt);
if (l <= tree[rt].mid) {
change(l, r, x, rt << 1);
}
if (tree[rt].mid < r) {
change(l, r, x, rt << 1 | 1);
}
pushup(rt);
}
int query(int l, int rt) {
if (tree[rt].l == tree[rt].r) {
return tree[rt].mi;
}
pushdown(rt);
if (l <= tree[rt].mid)
return query(l, rt << 1);
if (tree[rt].mid < l)
return query(l, rt << 1 | 1);
}
int n, m, a[N];
vector<int>v[N];
struct $${
int l, r, f, idx;
bool operator < (const $$ p) const {
if (l != p.l)
return l > p.l;
else if (f != p.f)
return f < p.f;
else
return r < p.r;
}
};
void solve() {
scanf("%d%d", &n, &m);
map<int,int>mp;
set<int>s;
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
s.insert(a[i]);
}
int cnt = 0;
for (int i : s) {
mp[i] = ++cnt;
}
for (int i = 1; i <= n; i++) {
v[mp[a[i]]].push_back(i);
}
vector<$$>vv;
for (int i : s) {
int pre = 0;
for (int j : v[mp[i]]) {
if (pre) {
vv.push_back($${pre, j, 0, 0});
}
pre = j;
}
}
for (int i = 1; i <= m; i++) {
int l,r;
scanf("%d%d", &l,&r);
vv.push_back($${l, r, 1, i});
}
sort(vv.begin(), vv.end());
build(1, n, 1);
vector<int>ans(m + 1);
for (auto p : vv) {
if (p.f) {
int k = query(p.r, 1);
if (k == 1e9) {
ans[p.idx] = -1;
} else {
ans[p.idx] = k;
}
} else {
change(p.r, n, p.r - p.l, 1);
}
}
for (int i = 1; i <= m; i++)
printf("%d\n", ans[i]);
}
int main() {
solve();
return 0;
}
Code will change the world !