10.21

打模拟赛疑似遇到了很典的题(因为它看着典),但原题机上找不到

属于代码难度低,思维含量高的题目,赛时拿了 \(50pts\) 暴力分

题目大意

给一个长度为 \(n\) 数列,记其价值为所有相邻数字差的绝对值的和

现在可以任意选择一段区间,将其翻转(即倒过来)

此操作至多进行一次,求操作后数列的最小价值

$ 4 \leq n \leq 5 \times 10^5 $

题解

很容易想到翻转操作只会影响翻转区间两边产生的价值

于是若翻转区间左边为 \(L\) ,右侧为 \(R\)

则翻转前其两侧价值计算为:

\[|A_{L}-A_{L-1}|+|A_{R}-A_{R+1}| \]

翻转后两侧价值计算为:

\[|A_{R}-A_{L-1}|+|A_{L}-A_{R+1}| \]

我们想要前者与后者的差最大

到这里就卡住了为什么赛时我还心心念念想用切比雪夫

利用一些数学直觉,会想到数轴

通过一些尝试,你会发现只有这两种情况下交换 \(L,R\) 会产生正价值

image

(可以手画一下其他情况)

总结一下,即当 \(A_{L}-A_{L-1}\)\(A_{R+1}-A_{R}\) 的正负性相同

\([A_{L-1},A_{L})\)\([A_{R},A_{R+1})\) 有交集时会产生正价值,价值为交集大小的两倍

(注:第二句话仅代表 \(A_{L-1}<A_{L}\) 的情况,另外一种情况交换式子中 \(A_{L-1},A_{L}\) 的顺序即可)

将问题成功转化为区间交问题,具体为分别选出相邻的数中前者小于后者的和后者小于前者的

分别构成两个区间集合,对这两个集分别求解其中某两个区间的最大交的大小,取两者最大值,在原序列价值上减去

你不会区间交???

CODE
#include<bits/stdc++.h>
#define fst first
#define sec second
#define abs(a) ((a)<0 ? -(a) : (a))
#define mkp(a,b) make_pair(a,b)
#define usetime() (double)clock () / CLOCKS_PER_SEC * 1000.0
using namespace std;
typedef long long LL;
typedef pair<int,int> pii;
const int maxn=2e5+5;
void read(int& x){
	char c;
	bool f=0;
	while((c=getchar())<48) f|=(c==45);
	x=c-48;
	while((c=getchar())>47) x=(x<<3)+(x<<1)+c-48;
	x=(f ? -x : x);
}
int n;
int a[maxn];
vector<pii> v;
int solve(){
	sort(v.begin(),v.end());
	int r=0,ans=0;
	for(int i=0;i<(int)v.size();i++){
		ans=max(ans,min(r,v[i].sec)-v[i].fst);
		r=max(r,v[i].sec);
	}
	return ans;
}
int main(){
	read(n);
	LL fl=0;
	for(int i=1;i<=n;i++){
		read(a[i]);
		fl+=(i==1 ? 0 : abs(a[i]-a[i-1]));
	}
	for(int i=1;i<n;i++){
		if(a[i]<a[i+1]){
			v.push_back(mkp(a[i],a[i+1]));
		}
	}
	int ans=solve();
	v.clear();
	for(int i=1;i<n;i++){
		if(a[i]>a[i+1]){
			v.push_back(mkp(a[i+1],a[i]));
		}
	}
	ans=max(ans,solve());
	printf("%lld",fl-2ll*ans);
	return 0;
}
//^o^
posted @ 2025-10-21 19:10  huangems  阅读(6)  评论(0)    收藏  举报