[ARC119E]Pancakes
壹、题目描述 ¶
贰、题解 ¶
遇到过一道可以转化到那上面的题。
首先有一种二位偏序的想法:
House from \(\sf XYX\)
算是非常经典的一道题吧(\(\sf ZXY\) 金句)
我们发现翻转一个区间 \([l,r]\),两边和中间的相邻数字差都不会变,只有左右端点附近会变,具体地,会令答案加上
\[|S_{r+1}-S_l|+|S_{r}-S_{l-1}|-(|S_{r+1}-S_{r}|+|S_l-S_{l-1}|) \]这里有四个绝对值,后面两个绝对值分别与 \(r\) 和 \(l\) 单独相关,前面两个就得分类讨论了。
我们不妨令 \(S_{r+1}=x,S_r=y\;,\;S_l=a,S_{l-1}=b\) ,那么前面两个绝对值就是
\[|x-a|+|y-b| \]我们想得到这个减去 \(|x-y|+|a-b|\) 的 最小值。
先鲁莽地讨论讨论(由于是取最值,下面全是可取等的也没问题了):
- \(x\geq a,y\geq b:\quad (x+y)-(a+b)\)
- \(x\geq a,y\leq b:\quad (x-y)-(a-b)\)
- \(x\leq a,y\geq b:\quad (y-x)-(b-a)\)
- \(x\leq a,y\leq b:\quad -(x+y)+(a+b)\)
然后我们其实没必要区分 \((x,y)\) 和 \((a,b)\) 的前后关系,因此其实可以省掉 \(\rm case\ 3,4\) ,只讨论 \(1,2\).
分类讨论后,这其实就是个二维偏序。随便用什么数据结构或者直接 \(\tt CDQ\) 分治都行。
复杂度 \(\mathcal O(n\log n)\).
具体地说,就是将 \((S_i,S_{i+1})\) 看成一个点 \(P\),对于另外的点 \(Q\) 看它和 \(P\) 的位置关系了,而这个位置关系则可以使用数据结构或者 \(\tt CDQ\) 分支解决。
但是还有一种更好的方法,请先了解 [CF1513F]Swapping Problem 的原理。
如果我们记 \(b_i=a_{i+1}\),那么我们的初始答案就是
考虑如果我们交换 \(i+1,j+1\) 这两个位置,那么我们的答案增加量就是
经过和上一道题类似的分类讨论之后,我们发现出现答案变优的情况只有 “同向相交”。
所以使用类似的处理方法就行了。
但是这样做过不了样例,为什么?考虑我们的 \(\Delta\) —— 在交换 \(i+1,j+1\) 这两个位置之后,它会对 \(i\) 产生影响,即 \(i\ge 1\),并且对于后面的数字也有影响,即 \(i+1+1\le n\Leftrightarrow i\le n-2\),所以,对于 \(i,j\),它们的取值范围都是 \([1,n-2]\),而我们的交换位置 \(i+1,j+1\) 就是 \([2,n-1]\),它们并没有包含 \(l=1\or r=n\) 的情况,对于这两种情况,别无他法,只能使用 \(\mathcal O(n)\) 进行暴力判断了。
总的时间复杂度还是 \(\mathcal O(n\log n)\),但是不需要任何数据结构或者高超的 \(\tt CDQ\) 分治!
叁、参考代码 ¶
const int maxn=300000;
const int inf=0x3f3f3f3f;
int a[maxn+5], n;
struct segment{
int l, r, t;
segment(){}
segment(int L, int R, int T): l(L), r(R), t(T){}
inline int operator <(const segment rhs) const{
return r>rhs.r;
}
}seg[maxn+5];
ll ans=0, ori=0;
inline void input(){
n=readin(1);
rep(i, 1, n) a[i]=readin(1);
int x, y;
rep(i, 1, n-1){
x=a[i], y=a[i+1];
if(x>y) seg[i]=segment(y, x, 1);
else seg[i]=segment(x, y, 0);
ans+=fab(x-y);
}
ori=ans;
}
int minn[2];
signed main(){
input();
sort(seg+1, seg+n+1);
minn[0]=minn[1]=inf;
int t, len=0;
rep(i, 1, n-1){
t=seg[i].t;
if(minn[t]<seg[i].r) len=max(len, seg[i].r-max(minn[t], seg[i].l));
minn[t]=min(minn[t], seg[i].l);
}
ans=ans-(len<<1ll);
rep(i, 2, n-1) ans=min(ans, ori-fab(a[i-1]-a[i])+fab(a[i-1]-a[n]));
rep(i, 2, n-1) ans=min(ans, ori-fab(a[i]-a[i+1])+fab(a[1]-a[i+1]));
printf("%lld\n", ans);
return 0;
}