Codeforces 1671-D: Insert a Progression
题目链接:Codeforces 1671-D: Insert a Progression
题目
题目截图
样例描述
题目大意
给定 x x x 和一个长度为 n n n 的数组 a i a_i ai,将 [ 1 , x ] [1, x] [1,x] 的整数都插入到数组中,可以插入一个数到数组中的任意位置(包括所有数前和所有数后)。设插入数之后的新数组为 a ′ a' a′,求 ∑ i = 1 n + x − 1 ∣ a i ′ − a i + 1 ′ ∣ \sum_{i=1}^{n+x-1}|a_{i}' - a_{i+1}'| ∑i=1n+x−1∣ai′−ai+1′∣ 的最小值。
题目解析
很容易发现,若将
z
∈
[
x
,
y
]
z \in [x, y]
z∈[x,y] 插入
x
,
y
x, y
x,y 之间,那么得到的差值绝对值和是不变的,即
∣
x
−
y
∣
=
∣
x
−
z
∣
+
∣
z
−
y
∣
|x-y|=|x-z|+|z-y|
∣x−y∣=∣x−z∣+∣z−y∣。这个现象其实还有更进一步的表现,对于数组
a
1
,
a
2
,
⋯
,
a
n
a_1,a_2,\cdots,a_n
a1,a2,⋯,an,若
a
1
≤
z
≤
a
n
a_1 \le z \le a_n
a1≤z≤an,那么存在
i
∈
(
1
,
n
]
i \in (1, n]
i∈(1,n] ,将
z
z
z 插入到
i
−
1
,
i
i-1,i
i−1,i 之间,将不会带来额外的差值绝对值和。因为假设我们随机在
(
1
,
n
)
,
n
>
2
(1,n),n>2
(1,n),n>2 之间选择一个位置
j
j
j,那么
z
∈
[
a
1
,
a
n
]
z \in [a_1,a_n]
z∈[a1,an] 必然在
a
1
,
a
j
a_1,a_j
a1,aj 或
a
j
,
a
n
a_j, a_n
aj,an 之间,这样我们可以不断地将整个区间的长度减小,最终只剩下两个数,也就满足了上述理论。
由于
1
,
x
1,x
1,x 都要插入到数组中,因此,这个问题就变成了一个更简单的问题:如何插入
1
,
x
1,x
1,x,使得目标最小(因为其它的数都可以对结果不带来任何的增加)。假设
1
1
1 在
x
x
x 之前,这样我们其实可以暴力查找
x
x
x 的位置,并记录在该位置之前带来最小增量的
1
1
1 的位置,并不断比较最小答案,特别地,要考虑
1
,
x
1,x
1,x 在同一个间隙及
1
1
1 在整个数组之前,
n
n
n 在整个数组之后的情况。 对于
1
1
1 在
x
x
x 之后的情况,我们可以把整个数组反转一下,重复上述过程即可。
Code
#include <bits/stdc++.h>
using namespace std;
#define ins(i,j,x) -abs(a[i]-a[j])+abs(a[i]-x)+abs(x-a[j])
typedef long long LL;
const int maxn = 2e5 + 7;
int a[maxn];
int main() {
int t, n, x;
cin >> t;
while(t--) {
cin >> n >> x >> a[1];
LL sum = 0, ans = 0x7fffffffffffffff;
for(int i=2; i<=n; ++i)
cin >> a[i], sum += abs(a[i] - a[i-1]);
for(int _=2; _>0; reverse(a+1, a+n+1), --_) {
ans = min(ans, sum + x - 1 + abs(a[1] - x));
int p1=abs(1-a[1]);
for(int i=2; i<=n; ++i) {
ans = min(ans, sum - abs(a[i] - a[i-1]) + abs(a[i-1] - 1) + abs(a[i] - x) + x - 1);
ans = min(ans, sum + p1 + ins(i-1, i, x));
p1 = min(p1, ins(i-1, i, 1));
}
ans = min(ans, sum + p1 + abs(x - a[n]));
}
cout << ans << endl;
}
return 0;
}```