题目链接:洛谷
这道题看起来是个期望题,但是其实是一道计算几何(这种题太妙了)
首先有一个很好的结论,在一个长度为$L$的数轴上,每次从$x$处出发,不停地走,有$frac{x}{L}$的概率从右端点掉下去,$frac{L-x}{L}$从左端点掉下去。
这个证明的话,感性理解一下。
令$l_x$表示从$x$处掉到左端点的概率,则$l_0=1,l_L=0$,且对于$xin (0,L)$,$l_x=frac{l_{x-1}+l_{x+1}}{2}$,所以$l_x$构成一个等差数列,所以得证。
显然,我们肯定是不能一直走的,不然得分肯定是0,但是我们可以“掉进”一些权值比较高的点使得答案最优,我们称这些点为“停止点”。
设从$x$处出发。
如果$x$本身就是“停止点”,那么答案就是$f_x$。($f_x$为这个点的权值)
否则$x$左右两侧的最近的“停止点”为$a,b$,这种策略的答案为$f_a*frac{b-x}{b-a}+f_b*frac{x-a}{b-a}$
我们发现它就是$(a,f_a),(b,f_b)$两点连接的线段在$x$处的$y$值。
所以我们维护对$(x,f_x)(xin [0,n+1])$这$n+2$个点计算出上凸包,然后就是直接贪心计算了。
1 #include<cstdio> 2 #define Rint register int 3 using namespace std; 4 typedef long long LL; 5 const int N = 100003; 6 struct Point { 7 LL x, y; 8 inline Point operator - (const Point &o) const {return (Point){x - o.x, y - o.y};} 9 inline LL operator * (const Point &o) const {return x * o.y - y * o.x;} 10 } p[N]; 11 int n, top; 12 LL a[N]; 13 inline void push(Point now){ 14 while(top > 1 && (now - p[top - 1]) * (p[top] - p[top - 1]) < 0) -- top; 15 p[++ top] = now; 16 } 17 int main(){ 18 scanf("%d", &n); 19 for(Rint i = 0;i <= n + 1;i ++){ 20 if(i && i <= n) scanf("%lld", a + i), a[i] *= 100000; 21 push((Point){i, a[i]}); 22 } 23 int now = 1; 24 for(Rint i = 1;i <= n;i ++){ 25 while(p[now].x < i) ++ now; 26 if(p[now].x == i) printf("%lld ", p[now].y); 27 else { 28 printf("%lld ", (LL) (1.0 * ((i - p[now - 1].x) * p[now].y + (p[now].x - i) * p[now - 1].y) / (p[now].x - p[now - 1].x))); 29 } 30 } 31 }