思路
-
很容易想到序列排序后,当前元素的左右两个元素至少有一个与当前元素差值是整个序列中最小的。
-
然后题目要找 m i n 1 ≤ j < i ∣ A i − A j ∣ min_{1≤j<i}|A_i−A_j| min1≤j<i∣Ai−Aj∣ 中最小的 A j A_j Aj ,那么将 A A A 排序后可能出现相邻下标大于当前的下标。
-
要解决这个问题就需要双向链表,从 A A A 序列的最后一个元素开始计算,计算完后直接删掉结点,下次计算即可不出现大于当前下标的邻点。
- a a a 数组表示链表的数据域,链表的结点顺序按 a a a 数组升序后的来。
- p p p 数组快速获得 A A A 序列第 x x x 个数在链表中的位置。
- l l l 与 r r r 数组表示链表结点的左右结点索引。
- 然后链表的左右两端有边界问题,这里放两个哨兵即可。
#include<iostream>
#include<algorithm>
#include<cmath>
#define x first
#define y second
using namespace std;
typedef long long LL;
const int N = 100010;
pair<LL,int> a[N], ans[N];
int p[N],l[N],r[N];
int main(){
cin.tie(0)->sync_with_stdio(false);
cout.tie(0);
int n; cin>>n;
for(int i = 1; i <= n; i++){
cin>>a[i].x;
a[i].y = i;
}
sort(a+1, a+1+n);
a[0].x = a[n+1].x = -4e9; //放两哨兵预防边界问题
for(int i = 1; i <= n; i++){
l[i] = i-1, r[i] = i+1;
p[a[i].y] = i; //映射位置
}
for(int i = n; i > 1; i--){
int j = p[i], left = l[j], right = r[j];
LL lv = abs(a[j].x - a[left].x);
LL rv = abs(a[j].x - a[right].x);
if(lv <= rv) ans[i] = {lv, a[left].y};
else ans[i] = {rv, a[right].y};
r[left] = right;
l[right] = left;
}
for(int i = 2; i <= n; i++) cout<< ans[i].x << ' ' << ans[i].y << '\n';
return 0;
}