[atARC130F]Replace by Average

记初始序列为$a_{i}$,答案序列为$b_{i}$(显然唯一),考虑如何求出$b_{i}$

引理1:$b_{i}$具有凸性(下凸)

考虑反证,若$b_{i}$不具有凸性,即存在$2b_{i}>b_{i-1}+b_{i+1}$,那么操作$(i-1,i,i+1)$也即可使得$b_{x}$减小,与最小性矛盾

引理2:$\forall p\in \Z,\min_{1\le i\le n}(a_{i}-pi)=\min_{1\le i\le n}(b_{i}-pi)$

考虑对$(i,j,k)$的操作,由最小性显然$a'_{j}\le a_{j}$,即该式单调不增

另一方面,显然有$\min(a_{i}-pi,a_{k}-pk)\le a'_{j}-pj$,即该式单调不减

综上,该式的值不因操作而变化,即得证

结论:记$c_{p}=\min_{1\le i\le n}(a_{i}-pi)$,则$\forall 1\le i\le n,b_{i}=\max_{p\in \Z}(c_{p}+pi)$

由引理2,显然$b_{i}-pi\ge c_{p}$,因此左式$\ge $右式

由引理1,取$p\in [b_{i}-b_{i-1},b_{i+1}-b_{i}]$,显然$b_{i}-pi$取到最小值,再结合引理2可得$b_{i}-i=c_{p}$,因此左式$\le $右式

综上,即得证

考虑$px+c_{p}$这条直线,假设$c_{p}$在$i_{0}$处取到最小值,代入即求经过$(i_{0},a_{i_{0}})$斜率为$p$的直线,那么再对每一个点$(i,a_{i})$找到最小和最大的$p$(使得$c_{p}$在其上取最小值),最后将所有直线求一个(上)凸包即可

关于每一个点的$p$,显然也即对所有点求(下)凸包,其中每一个点上一段斜率上取整和下一段的斜率下取整(其余点均不存在满足条件的$p$)

时间复杂度为$o(n)$,可以通过

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define N 300005
 4 #define ll long long
 5 struct Point{
 6     ll x,y;
 7 }P[N];
 8 struct Line{
 9     ll k,b;
10 }Q[N<<1];
11 int n,m,st[N<<1];
12 ll ans;
13 double get_k(Point x,Point y){
14     return 1.0*(x.y-y.y)/(x.x-y.x);
15 }
16 double get_x(Line x,Line y){
17     return -1.0*(x.b-y.b)/(x.k-y.k);
18 }
19 int main(){
20     scanf("%d",&n);
21     for(int i=1;i<=n;i++){
22         scanf("%lld",&P[i].y);
23         P[i].x=i;
24     }
25     for(int i=1;i<=n;i++){
26         while ((st[0]>1)&&(get_k(P[st[st[0]-1]],P[st[st[0]]])>=get_k(P[st[st[0]]],P[i])))st[0]--;
27         st[++st[0]]=i;
28     }
29     for(int i=1;i<=st[0];i++){
30         ll k1=-1e18,k2=1e18;
31         if (i>1)k1=ceil(get_k(P[st[i-1]],P[st[i]]));
32         if (i<st[0])k2=floor(get_k(P[st[i]],P[st[i+1]]));
33         if (k1>k2)continue;
34         if ((i>1)&&((!m)||(Q[m].k<k1)))Q[++m]=Line{k1,P[st[i]].y-k1*P[st[i]].x};
35         if ((i<st[0])&&((!m)||(Q[m].k<k2)))Q[++m]=Line{k2,P[st[i]].y-k2*P[st[i]].x};
36     }
37     st[0]=0;
38     for(int i=1;i<=m;i++){
39         while ((st[0]>1)&&(get_x(Q[st[st[0]-1]],Q[st[st[0]]])>=get_x(Q[st[st[0]]],Q[i])))st[0]--;
40         st[++st[0]]=i;
41     }
42     for(int i=1,j=1;i<=n;i++){
43         while ((j<st[0])&&(get_x(Q[st[j]],Q[st[j+1]])<i))j++;
44         ans+=Q[st[j]].k*i+Q[st[j]].b;
45     }
46     printf("%lld\n",ans);
47     return 0;
48 }
View Code
原文地址:https://www.cnblogs.com/PYWBKTDA/p/15618992.html