[ARC119E]Pancakes

280 篇文章 1 订阅

题目

传送门 to AtCoder

题目概要
有一个序列,定义其权值为相邻元素的差值的绝对值之和。现在可以整体翻转一个子区间,最大化权值。

数据范围与提示
n ≤ 3 × 1 0 5 n\le 3\times 10^5 n3×105 a i ≤ 1 0 9 a_i\le 10^9 ai109

思路

刚开始拿到这道题,我以为是树套树,暴力拆开绝对值。但是那样太麻烦,我就在想,有没有更简单一点的方法。

然后我有了这个 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) (ai1,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 。我把它放在二维坐标系里,主要是受到了二维偏序的想法的影响,不敢把它直接拍扁成一维……

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值