![洛谷P5155 [USACO18DEC]Balance Beam(期望,凸包) 洛谷P5155 [USACO18DEC]Balance Beam(期望,凸包)](https://image.shishitao.com:8440/aHR0cHM6Ly9ia3FzaW1nLmlrYWZhbi5jb20vdXBsb2FkL2NoYXRncHQtcy5wbmc%2FIQ%3D%3D.png?!?w=700&webp=1)
你以为它是一个期望dp,其实它是一个凸包哒!
设平衡木长度为\(L\),把向右走平衡木那个式子写一下:
\[dp[i]=\frac{dp[i+1]+dp[i-1]}{2}\]
然后会发现这是一个等差数列,显然有\(dp[0]=0,dp[L]=1\)
所以\(dp_{i\rightarrow L}=\frac{i}{L}\)
向左走同理:\(dp_{i\rightarrow 1}=\frac{L-i}{L}\)
令停止点为直接从这个点跳下去能得到期望报酬最高的点,设点\(i\)左右两端的停止点分别为\(l\)和\(r\)
则贡献\(f_i=a_l*\frac{r-i}{r-l}+a_r*\frac{i-l}{r-l}\),可以看成是\((l,a_l),(r,a_r)\)组成的线段与直线\(x=i\)的纵坐标
思考一下会发现所有停止点构成一个上凸包
式子写错调了我好久QAQ
代码:
#include <bits/stdc++.h>
#define N 100010
#define ll long long
#define db double
#define int long long
using namespace std;
int s[N],l[N],r[N];ll a[N];
bool pd(int x,int y,int z){ return (a[y]-a[x])*(z-y)<(a[z]-a[y])*(y-x); }
bool vis[N];
const ll bs=1e5;
main(){
int n,i,top=0,j;
scanf("%lld",&n);
for(i=1;i<=n;++i) scanf("%lld",&a[i]);
s[++top]=0;
for(i=1;i<=n+1;++i){
while(top>1 && pd(s[top-1],s[top],i)) top--;
s[++top]=i;
}
for(i=1;i<=n;++i) a[i]*=bs;
for(i=1;i<top;++i){
l[s[i]]=r[s[i]]=s[i];
for(j=s[i]+1;j<s[i+1] && j<=n;++j)
l[j]=s[i],r[j]=s[i+1];
}
for(int i=1;i<=n;++i){
if(l[i]==r[i])printf("%lld\n",a[i]);
else printf("%lld\n",(a[l[i]]*(r[i]-i)+a[r[i]]*(i-l[i]))/(r[i]-l[i]));
}
}