Codeforces Round #643 (Div. 2) E.Restorer Distance(三分)

题目

n(1<=n<=1e5)的数组a[],第i个数为ai(0<=ai<=1e9),每次操作你可以选以下三种中的一种

①选择i,令ai+1,代价为A

②选择i,令ai-1,代价为R

③选择i,j,令其中一个-1,另一个+1,代价为M

0<=A,R,M<=1e4,求最终的数组所有值都相等的最小代价

思路来源

https://www.cnblogs.com/1024-xzx/p/12926595.html

https://www.cnblogs.com/Kaike/p/12918815.html

题解

如果只有A和R,显然可以从一边往中间推,第i个向第i+1个转移时,

增量为i*A,减量为(n-i)*R,如果可以推就推,这样就能推到最小点,是开口向上的函数

类似的,若M合适,则最终会只使用AM或RM,同样是两个开口向上的函数

而最终的答案,是这三个函数取min,容易发现也是存在一个最小值点的,可三分

因为最小值只有一个点,这里记录一下三分的两种写法吧

 

官方题解给了一个口胡的证明,设上升个数p和下降个数q,分p<=q和p>q讨论

再设当前高度h,设当前小于h的个数为x,则大于等于的为x+1,

考虑h到h+1的变化,发现代价变化量为x的一次函数,而x发生变化当且仅当在初始高度分界点

说明当p<=q时为若干段分段线性函数,p>q同理,

题解还说明当二者之间变化时还会出现一个极值点\frac{\sum a_{i}}{n}(没太看懂),和所有初始分界点(极值点)取小即可

 

另外,通过以上分析,可以判断是一个连续函数导数线性变化,最多变号一次的单峰函数,

那么分段函数也有如此特性,结合题目性质发现开口向上,果断三分

代码1

#include
using namespace std;
typedef long long ll;
const int N=1e5+10;
int n,add,sub,mix,h[N],l,r;
ll cal(int x){
    ll y=0,z=0,sm,v;//add sub
    for(int i=1;i<=n;++i){
        if(h[i]2){//考虑[1,3]时m=mm 但1为最小点
        int m=(l+r)/2,mm=(m+r)/2;//m=mm时有r-l<=2
        if(cal(m)

代码2

#include
using namespace std;
typedef long long ll;
const int N=1e5+10;
int n,add,sub,mix,h[N],l,r;
ll cal(int x){
    ll y=0,z=0,sm,v;//add sub
    for(int i=1;i<=n;++i){
        if(h[i]

 

你可能感兴趣的:(二分/三分/尺取/双指针)