题目
题目概要
有一个序列,定义其权值为相邻元素的差值的绝对值之和。现在可以整体翻转一个子区间,最大化权值。
数据范围与提示
n
≤
3
×
1
0
5
n\le 3\times 10^5
n≤3×105 而
a
i
≤
1
0
9
a_i\le 10^9
ai≤109 。
思路
刚开始拿到这道题,我以为是树套树,暴力拆开绝对值。但是那样太麻烦,我就在想,有没有更简单一点的方法。
然后我有了这个
o
b
s
e
r
v
a
t
i
o
n
\rm observation
observation,把绝对值换成曼哈顿距离。记
ℓ
\ell
ℓ 为直线方程
x
=
y
x=y
x=y 对应的直线,
p
i
p_i
pi 为点
(
a
i
−
1
,
a
i
)
(a_{i-1},a_i)
(ai−1,ai),那么我们只需要求
max
d
i
s
(
p
l
,
ℓ
)
+
d
i
s
(
p
r
+
1
,
ℓ
)
−
d
i
s
(
p
l
,
p
r
+
1
)
\max dis(p_l,\ell)+dis(p_{r+1},\ell)-dis(p_l,p_{r+1})
maxdis(pl,ℓ)+dis(pr+1,ℓ)−dis(pl,pr+1)
即交换一次能够获得的最大差值。这里的 d i s dis dis 都定义为曼哈顿距离。由于 l , r l,r l,r 对称,不需要 l < r l<r l<r 这种条件。
不妨只讨论 p l p_l pl 在 ℓ \ell ℓ 下方的情况。不妨规定 p r + 1 p_{r+1} pr+1 在 p l p_l pl 右侧(因为我们实际上会检测每一对点),简单推导(画图是强大的工具)可知,只需要让 p r + 1 p_{r+1} pr+1 最矮即可。这里还是贴一下图吧。
- 情况一:
p
j
p_j
pj 高度适中。红色为正(加上红色线段的长度),绿色为负(减去绿色线段的长度)。为了最大化
d
1
d_1
d1,当然要让
p
j
p_j
pj 尽量矮。
- 情况二:
p
j
p_j
pj 相当矮。红绿色的含义同上。为了方便观察,两条红线没有画重叠。显然这是个定值
2
d
i
s
(
p
i
,
ℓ
)
2\;dis(p_i,\ell)
2dis(pi,ℓ),并且必定比上面的
2
d
1
2d_1
2d1 大。
- 情况三:
p
j
p_j
pj 相当高。无论它是在
ℓ
\ell
ℓ 上方还是下方,都会得到一个负数,显然不优。
所以最终的代码超级简单。只需要记得,把 x , y x,y x,y 翻转再做一次——即考虑 ℓ \ell ℓ 上方的点。只用了 s o r t \rm sort sort,复杂度 O ( n log n ) \mathcal O(n\log n) O(nlogn) 。
代码
对了,还有 l = 1 l=1 l=1 或 r = n r=n r=n 的情况,没有被涵盖进去,请特判。
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
# define rep(i,a,b) for(int i=(a); i<=(b); ++i)
# define drep(i,a,b) for(int i=(a); i>=(b); --i)
typedef long long int_;
inline int_ readint(){
int_ a = 0; char c = getchar(), f = 1;
for(; c<'0'||c>'9'; c=getchar())
if(c == '-') f = -f;
for(; '0'<=c&&c<='9'; c=getchar())
a = (a<<3)+(a<<1)+(c^48);
return a*f;
}
inline void writeint(int_ x){
if(x > 9) writeint(x/10);
putchar((x-x/10*10)^48);
}
const int MaxN = 300005;
const int infty = (1<<30)-1;
inline int ABS(const int &x){
return x < 0 ? -x : x;
}
struct Point{
int x, y;
bool operator < (const Point &t) const {
return x < t.x;
}
};
Point p[MaxN];
int main(){
int n = readint();
int lst = readint();
int_ ans = 0; // init
for(int i=2; i<=n; ++i){
p[i-1].x = lst;
lst = p[i-1].y = readint();
ans += ABS(p[i-1].x-lst);
}
int now = 0; // max delta
for(int i=1; i<n; ++i) // when r = n
now = max(now,ABS(p[i].x-p[i].y)-ABS(p[n-1].y-p[i].x));
for(int i=1; i<n; ++i) // when l = 1
now = max(now,ABS(p[i].x-p[i].y)-ABS(p[1].x-p[i].y));
sort(p+1,p+n); // at descending of x
for(int i=n-1,vy=infty; i>=1; --i){
if(p[i].y <= p[i].x){ // below the line
if(vy <= p[i].y)
now = max(now,2*ABS(p[i].x-p[i].y));
else if(vy <= p[i].x)
now = max(now,2*ABS(p[i].x-vy));
vy = min(vy,p[i].y);
}
swap(p[i].x,p[i].y);
}
sort(p+1,p+n);
for(int i=n-1,vy=infty; i>=1; --i)
if(p[i].y <= p[i].x){
if(vy <= p[i].y)
now = max(now,2*ABS(p[i].x-p[i].y));
else if(vy <= p[i].x)
now = max(now,2*ABS(p[i].x-vy));
vy = min(vy,p[i].y);
}
printf("%lld\n",ans-now);
return 0;
}
后记
跟我一个机房的人都比我强 😢 我直接膜拜 A r e x t r e \sf Arextre Arextre 大巨佬 % % % \%\%\% %%%
他说此题与本题本质相同,只需要把 ( x , y ) (x,y) (x,y) 当成区间放在数轴上,分析更 e a s y \rm easy easy 。我把它放在二维坐标系里,主要是受到了二维偏序的想法的影响,不敢把它直接拍扁成一维……