洛谷P5155 [USACO18DEC]Balance Beam(期望,凸包)

你以为它是一个期望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])); 
    }
}

猜你喜欢

转载自www.cnblogs.com/PsychicBoom/p/10725574.html