给出同一个水平线上的 n n 个位置 Xi X i ,依次递增,
任意2个位置之间都可以配对,花费为距离差,每个位置最多仅能被配对一次,你要给 K K 对位置配对,问 K K 对位置的距离差之和最少是多少。
2≤n≤100000 2 ≤ n ≤ 100000
1≤k≤n/2 1 ≤ k ≤ n / 2
0≤Xi≤1000000000 0 ≤ X i ≤ 1000000000
显然,我们配对的 K K 对位置,
他们必定满足每一对都是相邻的 2 2 个位置,
否则必定会有更优的答案,
那么我们可以处理出所有相邻位置之间的距离差,
即 D1,D2,D3……Dn−2,Dn−1 D 1 , D 2 , D 3 … … D n − 2 , D n − 1
如果 K=1 K = 1 ,那么显然,答案为 min m i n { Dj D j }
如果 K−2 K − 2 ,那么答案就有 2 2 种可能,
因为选了任意一个 Dj D j 以后, Dj−1 D j − 1 , Dj+1 D j + 1 不能被选,
所以第一种可能就是选了最小值 Di D i ,以及除了 Di−1,Di,Di+1以外的最小值 D i − 1 , D i , D i + 1 以 外 的 最 小 值 。
第二种可能就是同时选了 Di−1 D i − 1 和 Di+1 D i + 1 。
因为如果你不选了 Di D i 而且仅在了 Di−1 D i − 1 或者 Di+1 D i + 1 中的某一个,那么你将那个换成 Di D i 肯定会更优。
那么我们就可以知道,最小值两边要么都不选,要么都选
所以我们可以这样做,
每次找到一个最小的 Dj D j 加入,
设这个最小值为 Di D i ,
然后删除 Di−1,Di,Di+1 D i − 1 , D i , D i + 1 ,并且加入一个点权为 Di−1+Di+1−Di D i − 1 + D i + 1 − D i 的点,就是多一次反悔的机会,如果到时候我们发现选 Di+1 D i + 1 跟选 Di−1 D i − 1 更优,那么我们就可以将那个位置跟这个位置同时给 Di−1 D i − 1 , Di+1 D i + 1 ,同时将 Di D i 剔除。
然后考虑边界,
如果最小值在边上,那么我们可以发现,它必定是一定要被选到的,这个很容易理解,因为如果我们选了 2 2 个,
设为 Dx+Dy D x + D y ,那么就算其中一个因为与它相邻而不能被选,那么另外一个也必定能与它相加,那么我们从而使结果更小。
然后删除的过程可以利用链表实现,
而因为 Dj D j 有 n−1 n − 1 个,而 n n 有 105 10 5 ,那么我们就要利用一些数据结构去维护它的最小值,可是因为有删除操作,比较麻烦,所以我写的是线段树去维护。
#include
#include
#include
#include
#define N 100005
using namespace std;
typedef long long ll;
struct Node { ll num; int pre, nxt; }d[N];
int tree[5*N], n, k;
void Build(int x, int l, int r)
{
if (l == r)
{
tree[x] = l;
return;
}
int mid = (l + r) >> 1;
Build(x * 2, l, mid);
Build(x * 2 + 1, mid + 1, r);
if (d[tree[x * 2]].num > d[tree[x * 2 + 1]].num) tree[x] = tree[x * 2 + 1];
else tree[x] = tree[x * 2];
}
void Change(int x, int l, int r, int z)
{
if (l == r) return;
int mid = (l + r) >> 1;
if (z <= mid) Change(x * 2, l, mid, z);
else Change(x * 2 + 1, mid + 1, r, z);
if (d[tree[x * 2]].num > d[tree[x * 2 + 1]].num) tree[x] = tree[x * 2 + 1];
else tree[x] = tree[x * 2];
}
int main()
{
scanf("%d %d", &n, &k);
int x, y;
ll Max_num = 0;
scanf("%d", &y);
for (int i = 1; i < n; i++)
{
scanf("%d", &x);
d[i].num = x - y,
d[i].pre = i - 1,
d[i].nxt = i + 1,
Max_num += d[i].num;
y = x;
}
Max_num += 5;
d[n].pre = n - 1;
Build(1, 1, n - 1);
ll ans = 0;
for (int i = 1; i <= k; i++)
{
x = tree[1];
ans += d[x].num;
if (d[x].pre == 0 && d[x].nxt == n) break;
int xpre = d[x].pre;
int xnxt = d[x].nxt;
if (xpre == 0)
{
d[d[xnxt].nxt].pre = 0;
d[xnxt].num = Max_num;
d[x].num = Max_num;
Change(1, 1, n - 1, x);
Change(1, 1, n - 1, xnxt);
} else
if (xnxt == n)
{
d[d[xpre].pre].nxt = n;
d[xpre].num = Max_num;
d[x].num = Max_num;
Change(1, 1, n - 1, x);
Change(1, 1, n - 1, xpre);
} else
{
d[x].num = d[xpre].num + d[xnxt].num - d[x].num;
d[d[xpre].pre].nxt = x;
d[d[xnxt].nxt].pre = x;
d[x].pre = d[xpre].pre;
d[x].nxt = d[xnxt].nxt;
d[xpre].num = Max_num;
d[xnxt].num = Max_num;
Change(1, 1, n - 1, x);
Change(1, 1, n - 1, xpre);
Change(1, 1, n - 1, xnxt);
}
}
printf("%lld\n", ans);
return 0;
}