链接
https://ac.nowcoder.com/acm/contest/8282/C
题意
一个长度为 $n$ 的序列,定义一次操作要选定一个 $i\in(1,n)$,然后将序列中第 $i$ 个数变成与它相邻的两个数的平均数。
在可以进行无限次任意位置的操作的情况下,求能得到的序列最小总和。
思路
将 $i$ 看作平面直角坐标系上的一个点 $(i,a_i)$。
在操作完后,所有的点会变成一个下凸壳的形状。
因此,只要求出下凸壳,两点之间对纵坐标求等差数列和就行。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| #include <bits/stdc++.h> #define SZ(x) (int)(x).size() #define ALL(x) (x).begin(),(x).end() #define PB push_back #define EB emplace_back #define MP make_pair #define FI first #define SE second using namespace std; typedef double DB; typedef long long LL; typedef pair<int,int> PII; typedef vector<int> VI; typedef vector<PII> VPII;
const double EPS=1e-8; const int N=1e6+5; int n,top; int sgn(double x) {return fabs(x)<EPS?0:(x>0?1:-1);} struct P { double x,y; P() {} P(double x,double y):x(x),y(y) {}
bool operator == (const P &a) const {return !sgn(x-a.x)&&!sgn(y-a.y);} bool operator < (const P &a) const {return sgn(x-a.x)<0||sgn(x-a.x)==0&&sgn(y-a.y)<0;}
P operator - (const P &a) const {return P(x-a.x,y-a.y);}
double operator ^ (const P &a) const {return x*a.y-y*a.x;}
double dist(P p) {return hypot(x-p.x,y-p.y);} }p[N],stk[N]; int main() { scanf("%d",&n); for(int i=1;i<=n;i++) { DB y; scanf("%lf",&y); p[i]=P(i,y); } sort(p+1,p+1+n); for(int i=1;i<=min(n,2);i++) stk[++top]=p[i]; for(int i=3;i<=n;i++) { while(top>=2&&sgn((stk[top]-stk[top-1])^(p[i]-stk[top]))<=0) --top; stk[++top]=p[i]; } double res=stk[1].y; for(int i=2;i<=top;i++) { double d=(stk[i].y-stk[i-1].y)/(stk[i].x-stk[i-1].x); res+=(stk[i].x-stk[i-1].x)*(stk[i-1].y+d)+(stk[i].x-stk[i-1].x)*(stk[i].x-stk[i-1].x-1)*d/2; } printf("%.10f\n",res); return 0; }
|