[CF522D]Closest Equals
题目大意:给一个区间,多次询问,每次问区间$[l,r]$里最近的两个相同的数的距离是多少。
题解:用一个数组$pre_i$表示第$i$个数前面最近的一个相同的数在哪,询问变成了询问$[l,r]$中$i-pre_i$的最小值,且$pre_i\in[l,r]$。难度就在处理$pre_i\not\in[l,r]$上。
$$
\because pre_i<i,i\in[l,r]\\
\therefore pre_i<r\\
若pre_i\not\in[l,r]\\
则pre_i<l
$$
这题没有修改,可以把询问按$l$排序,把不合法的点贡献去掉
卡点:$map$没更新
C++ Code:
#include <cstdio> #include <algorithm> #include <map> #define maxn 500010 const int inf = 0x3f3f3f3f; inline int min(int a, int b) {return a < b ? a : b;} int a[maxn], pre[maxn], nxt[maxn]; std::map<int, int> mp; struct Query { int l, r, id; inline bool operator < (const Query &rhs) const {return l < rhs.l;} } Q[maxn]; int ans[maxn]; int V[maxn << 2]; inline void update(int rt) { V[rt] = min(V[rt << 1], V[rt << 1 | 1]); } void build(int rt, int l, int r) { if (l == r) { if (pre[l]) V[rt] = l - pre[l]; else V[rt] = inf; return ; } int mid = l + r >> 1; build(rt << 1, l, mid); build(rt << 1 | 1, mid + 1, r); update(rt); } void modify(int rt, int l, int r, int p) { if (l == r) { V[rt] = inf; return ; } int mid = l + r >> 1; if (p <= mid) modify(rt << 1, l, mid, p); else modify(rt << 1 | 1, mid + 1, r, p); update(rt); } int query(int rt, int l, int r, int L, int R) { if (L <= l && R >= r) return V[rt]; int mid = l + r >> 1, ans = inf; if (L <= mid) ans = query(rt << 1, l, mid, L, R); if (R > mid) ans = min(ans, query(rt << 1 | 1, mid + 1, r, L, R)); return ans; } int n, m; int main() { scanf("%d%d", &n, &m); for (int i = 1; i <= n; i++) { scanf("%d", a + i); if (mp.count(a[i])) pre[i] = mp[a[i]], nxt[mp[a[i]]] = i, mp[a[i]] = i; else pre[i] = 0, mp[a[i]] = i; } for (int i = 1; i <= m; i++) { scanf("%d%d", &Q[i].l, &Q[i].r); Q[i].id = i; } std::sort(Q + 1, Q + m + 1); build(1, 1, n); int last = 1; for (int i = 1; i <= m; i++) { int l = Q[i].l; for (; last < l; last++) { if (nxt[last] >= l) modify(1, 1, n, nxt[last]); } ans[Q[i].id] = query(1, 1, n, l, Q[i].r); } for (int i = 1; i <= m; i++) if (ans[i] != inf) printf("%d\n", ans[i]); else puts("-1"); return 0; }