1 条题解
-
0
作为勤劳的题解自动姬来写这篇题解。
仅我自己认为,想到凸包的过程是这道题的难点,凸包以后容易解决,相反不是难点。所以这篇题解会主要讲解如何想到凸包的过程。
假设 。首先注意到最基本的情况,即 ,序列容易调整至 的形态。
接下来处理特殊情况 ,容易发现能调整到差分数组形如 。
那我们便猜测对于任意的 ,是否都可以满足可以调整到差分数组形如 。事实证明是可以满足的。对于差分数组 ,若存在 满足 ,那么必然可以将实际中间的数调整到更小。最终总是可以调整到形如 。而这个形态也是最优的,因为此时差分数组并不存在 。由于这里不是 MO 性质的题目,所以不放严格证明,读者可以试着自证。
那也就是需要找到一个长度为 的序列 ,使得 。而对于每一段 ,形如上文的形式调整。将其视作平面问题,类似有 段线段拼接在一起。可是要如何找到最优的答案呢?对于某些决策 , 优于 的充要条件在平面上大致可看做是 ,即形成了上凸包,显然可以消掉凸的部分使总和最小。那我们可以找到极优解即在任何一个子区间内没有上凸的部分。即下凸包。对于总体而言,我们需要找到这 个点的近似下凸包。为什么说近似呢?因为这里的斜率判断是根据两段差分数组的首尾元素的大小。令 和 是相邻两个决策段, 为第 段的差分数组头元素, 为第 段的差分数组尾元素,那么 和 共存(即不用合成同一个决策段)的等价条件是 。
那么下凸包可以用一个单调栈简单维护。代码很好写,细节有点多,注意处理 corner case。
#include<bits/stdc++.h> #define ll long long using namespace std; const int N=3e5+5; int n; ll a[N]; ll Abs(ll x){ return max(x,-x); } ll slope(int i,int j,int type){ ll k=Abs(a[i]-a[j]),l=j-i; ll res; if(a[i]<=a[j]){ if(type==0){ res=k/l; }else{ res=(k+l-1)/l; } }else{ if(type==0){ res=-(k+l-1)/l; }else{ res=-k/l; } } return res; } ll F(ll n){ return n*(n+1)/2; } ll getsum(int i,int j){ ll k=Abs(a[i]-a[j]),l=j-i,minn=min(a[i],a[j]); return minn*(l+1)+F(l)*(k/l)+F(k%l); } int sta[N],top; int main(){ scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%lld",&a[i]); } sta[++top]=1; for(int i=2;i<=n;i++){ while(top>=2&&slope(sta[top-1],sta[top],1)>slope(sta[top],i,0)){ --top; } sta[++top]=i; } ll res=0; for(int i=2;i<=top;i++){ res+=getsum(sta[i-1],sta[i]); } for(int i=2;i<top;i++){ res-=a[sta[i]]; } printf("%lld\n",res); return 0; }
- 1
信息
- ID
- 2520
- 时间
- 2000ms
- 内存
- 1024MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者