最开始我们先旋转一下这张桌子,使得 A 1 = A n + 1 = max { A i } A_1=A_{n+1}=\max\{A_i\} A1=An+1=max{Ai}。
这是非常有效的,因为我们把环就变成链,只要到达了链的任意一端 1 / n + 1 1/n+1 1/n+1 就肯定会结束游戏。
定义 E i : E_i: Ei: 从 i i i 开始游戏,运用最优策略,结束游戏的期望收益。
显然, E 1 = A 1 , E n + 1 = A n + 1 , ∀ 1 ≤ i ≤ n E i = max ( A i , E i − 1 + E i + 1 2 − B i ) E_1=A_1,E_{n+1}=A_{n+1},\forall_{1\le i\le n}E_{i}=\max\Big(A_{i},\frac{E_{i-1}+E_{i+1}}2-B_i\Big) E1=A1,En+1=An+1,∀1≤i≤nEi=max(Ai,2Ei−1+Ei+1−Bi)。
简化问题,假设没有这个 B i B_i Bi 存在。
把 i i i 看成 ( i , A i ) (i,A_i) (i,Ai) 放到二维平面内,则答案 E i E_i Ei 就是 i i i 为横坐标时在这些点维护出的凸壳上的纵坐标。
画画图就知道了。
维护凸壳以及求解答案是可以在线性时间内完成的。
考虑将当前复杂问题往简单问题上靠拢。
能否构造出一个新形式的 F i F_i Fi 将 B i B_i Bi 抵消掉,即 F i = max ( G i , F i − 1 + F i + 1 2 ) F_i=\max\Big(G_i,\frac{F_{i-1}+F_{i+1}}2\Big) Fi=max(Gi,2Fi−1+Fi+1),再通过 F i F_i Fi 反推回 E i E_i Ei。
E i − C i = max ( A i − C i , E i − 1 + E i + 1 2 − C i − B i ) ⇕ E_i-C_i=\max\Big(A_i-C_i,\frac{E_{i-1}+E_{i+1}}2-C_i-B_i\Big)\\ \Updownarrow Ei−Ci=max(Ai−Ci,2Ei−1+Ei+1−Ci−Bi)⇕ E i − C i = max ( A i − C i , E i − 1 − C i − 1 + E i + 1 − C i + 1 2 + C i − 1 + C i + 1 2 − C i − B i ) E_i-C_i=\max\Big(A_i-C_i,\frac{E_{i-1}-C_{i-1}+E_{i+1}-C_{i+1}}2+\frac{C_{i-1}+C_{i+1}}2-C_i-B_i\Big) Ei−Ci=max(Ai−Ci,2Ei−1−Ci−1+Ei+1−Ci+1+2Ci−1+Ci+1−Ci−Bi)
(因为 B i B_i Bi 是常量,前面的系数是不带 i i i 的任一次方的,所以我们构造的时候应该先考虑不出现带 i i i 系数的形式)
发现只要令 ∀ 1 ≤ i ≤ n C i − 1 + C i + 1 2 − C i − B i = 0 \forall_{1\le i\le n}\frac{C_{i-1}+C_{i+1}}2-C_i-B_i=0 ∀1≤i≤n2Ci−1+Ci+1−Ci−Bi=0 就能达到目的。
⇒ C i − C i − 1 + 2 B i = C i + 1 − C i \Rightarrow C_i-C_{i-1}+2B_i=C_{i+1}-C_i ⇒Ci−Ci−1+2Bi=Ci+1−Ci 且 C 2 = C 1 = 0 C_2=C_1=0 C2=C1=0,这样 C C C 就递推出来了。
令 F i = E i − C i F_i=E_i-C_i Fi=Ei−Ci,则 F i = max ( A i − C i , F i − 1 + F i + 1 2 ) F_i=\max\Big(A_i-C_i,\frac{F_{i-1}+F_{i+1}}2\Big) Fi=max(Ai−Ci,2Fi−1+Fi+1),以 ( i , F i ) (i,F_i) (i,Fi) 构造凸壳即可线性求解。
先维护出凸壳,后再挨个求纵坐标。
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define double long double
#define maxn 200005
int A[maxn], B[maxn], C[maxn], s[maxn];
double ans;
int n;
signed main() {
scanf( "%lld", &n );
for( int i = 1;i <= n;i ++ ) scanf( "%lld", &A[i] );
for( int i = 1;i <= n;i ++ ) scanf( "%lld", &B[i] );
int id = max_element( A + 1, A + n + 1 ) - A;
rotate( A + 1, A + id, A + n + 1 );
rotate( B + 1, B + id, B + n + 1 );
A[n + 1] = A[1], B[n + 1] = B[1];
for( int i = 3;i <= n + 1;i ++ ) C[i] = ( B[i - 1] + C[i - 1] << 1 ) - C[i - 2];
for( int i = 1;i <= n + 1;i ++ ) A[i] -= C[i];
int top = 0;
s[++ top] = 1;
for( int i = 2;i <= n + 1;i ++ ) {
while( top and (A[i] - A[s[top]]) * (s[top] - s[top - 1]) > (A[s[top]] - A[s[top - 1]]) * (i - s[top]) ) top --;
/*
( A[i]-A[s[top]] ) / ( i-s[top] ) k1 of new line
( A[s[top]]-A[s[top-1]] ) / ( s[top]-s[top-1]) k1 of latest line
if k1>k2 throw k2
*/
s[++ top] = i;
}
double ans = 0;
top = 0;
for( int i = 1;i <= n;i ++ ) {
if( s[top + 1] == i ) top ++;
ans += (A[s[top + 1]] - A[s[top]]) * 1.0 / (s[top + 1] - s[top] ) * (i - s[top]) + A[s[top]];
//将凸包上的一条边抽变成从原点开始计算
ans += C[i];
}
printf( "%.12Lf\n", ans / n );
return 0;
}