邻值查找
题意
给定一个长度为 n n n 的序列 A A A, A A A 中的数各不相同。
对于 A A A 中的每一个数 A i A_i Ai,求:
m
i
n
1
≤
j
<
i
∣
A
i
−
A
j
∣
min_{1≤j<i}|A_i−A_j|
min1≤j<i∣Ai−Aj∣
以及令上式取到最小值的
j
j
j(记为
P
i
P_i
Pi)。若最小值点不唯一,则选择使
A
j
A_j
Aj 较小的那个。
思路
将原数组按照权值递增排序后,构造成一个链表,然后按照输入顺序从后往前计算结果,每算出一个数
A
i
A_i
Ai的结果后,就将其从链表中删除,这样可以保证计算第 i
个数时,所有还存在的数在输入中一定在其前面 满足了题目要求的 1<=j<i
实现细节请看代码注释
代码
#include<bits/stdc++.h>
#define int long long
#define INF 0x3f3f3f3f
#define mod 1000000007
#define MOD 998244353
#define rep(i, st, ed) for (int (i) = (st); (i) <= (ed);++(i))
#define pre(i, ed, st) for (int (i) = (ed); (i) >= (st);--(i))
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
template<typename T> inline T gcd(T a, T b) { return b ? gcd(b, a % b) : a; }
template<typename T> inline T lowbit(T x) { return x & -x; }
const int N = 1e5 + 10;
int n;
PII a[N]; // 记录每个数的权值和输入的下标
PII res[N]; // 记录答案
int p[N]; // 记录每个数排序后的下标
int l[N], r[N]; // 前向指针和后向指针数组
void solve() {
cin >> n;
rep(i, 1, n) {
cin >> a[i].first; // 读入权值
a[i].second = i; // 记录下标
}
sort(a + 1, a + 1 + n); // 按权值递增排序
rep(i, 1, n) {
l[i] = i - 1; // 对排序后的数据构造链表
r[i] = i + 1;
p[a[i].second] = i; // 记录排序前的下标对应的排序后的下标
}
a[0].first = a[n + 1].first = LLONG_MAX; // 边界
for (int i = n; i > 1; --i) { // 按输入顺序从后往前计算答案
int j = p[i]; // j 表示第 i 个输入的数排序后在链表中的位置(下标)
int left = l[j], right = r[j]; // 当前数在链表中的前驱节点和后继节点的下标
int v = a[j].first; // 当前节点的权值
int lv = abs(a[left].first - v); // 与左边节点的差的绝对值
int rv = abs(a[right].first - v); // 与右边节点的差的绝对值
if (lv <= rv) { // 如果当前数与 左边的数的差值 小于 与右边的数的插值 按照题目要求选择左边的数
res[i] = { lv,a[left].second };
// a[left].second 表示左边的数在原数组中的下标
}
else { // 否则选择右边的点
res[i] = { rv,a[right].second };
}
// 将当前节点从链表中删除
l[right] = left;
r[left] = right;
}
// 按输入顺序输出答案
rep(i, 2, n) {
printf("%lld %lld\n", res[i].first, res[i].second);
}
}
signed main() {
// int t; cin >> t;
// while (t--)
solve();
return 0;
}