最开始我们先旋转一下这张桌子,使得 A1=An+1=max{Ai}A_1=A_{n+1}=\max\{A_i\}A1=An+1=max{Ai}。
这是非常有效的,因为我们把环就变成链,只要到达了链的任意一端 1/n+11/n+11/n+1 就肯定会结束游戏。
定义 Ei:E_i:Ei: 从 iii 开始游戏,运用最优策略,结束游戏的期望收益。
显然,E1=A1,En+1=An+1,∀1≤i≤nEi=max(Ai,Ei−1+Ei+12−Bi)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)。
简化问题,假设没有这个 BiB_iBi 存在。
把 iii 看成 (i,Ai)(i,A_i)(i,Ai) 放到二维平面内,则答案 EiE_iEi 就是 iii 为横坐标时在这些点维护出的凸壳上的纵坐标。
画画图就知道了。
维护凸壳以及求解答案是可以在线性时间内完成的。
考虑将当前复杂问题往简单问题上靠拢。
能否构造出一个新形式的 FiF_iFi 将 BiB_iBi 抵消掉,即 Fi=max(Gi,Fi−1+Fi+12)F_i=\max\Big(G_i,\frac{F_{i-1}+F_{i+1}}2\Big)Fi=max(Gi,2Fi−1+Fi+1),再通过 FiF_iFi 反推回 EiE_iEi。
Ei−Ci=max(Ai−Ci,Ei−1+Ei+12−Ci−Bi)⇕E_i-C_i=\max\Big(A_i-C_i,\frac{E_{i-1}+E_{i+1}}2-C_i-B_i\Big)\\ \UpdownarrowEi−Ci=max(Ai−Ci,2Ei−1+Ei+1−Ci−Bi)⇕Ei−Ci=max(Ai−Ci,Ei−1−Ci−1+Ei+1−Ci+12+Ci−1+Ci+12−Ci−Bi)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)
(因为 BiB_iBi 是常量,前面的系数是不带 iii 的任一次方的,所以我们构造的时候应该先考虑不出现带 iii 系数的形式)
发现只要令 ∀1≤i≤nCi−1+Ci+12−Ci−Bi=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 就能达到目的。
⇒Ci−Ci−1+2Bi=Ci+1−Ci\Rightarrow C_i-C_{i-1}+2B_i=C_{i+1}-C_i⇒Ci−Ci−1+2Bi=Ci+1−Ci 且 C2=C1=0C_2=C_1=0C2=C1=0,这样 CCC 就递推出来了。
令 Fi=Ei−CiF_i=E_i-C_iFi=Ei−Ci,则 Fi=max(Ai−Ci,Fi−1+Fi+12)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,Fi)(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 lineif 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;
}