【洛谷5155】[USACO18DEC] Balance Beam P(期望+凸壳)

点此看题面

  • 给定一个长度为(n)的序列(a)
  • 当你处于某个位置(i)的时候,你可以选择结束游戏并获得(a_i)的报酬,也可以不结束游戏随机走向(i-1)(i+1),如果走到(0)(n+1)则只能结束游戏且无法获得任何报酬。
  • 对于每个起始位置,求出最优策略下的期望报酬。
  • (nle10^5)

序列游走问题

感觉我之前绝对接触过类似的套路,但翻了翻博客没找到。。。

(f_i)表示从(i)出发的最大期望报酬,转移方程:

[f_i=max{frac{f_{i-1}+f_{i+1}}2,a_i} ]

这个方程不但成环,还有个(max),显然无法用一般(DP)套路或是高斯消元之类的常规期望算法解决。

但由于(f_i)只有两种取值,考虑一个区间([j,k]),假设(f_j=a_j,f_k=a_k),且(forall iin(j,k))(f_i=frac{f_{i-1}+f_{i+1}}2)

对于(f_i=frac{f_{i-1}+f_{i+1}}2),我们把(2)乘到左边并移项变形,得到:

[f_i-f_{i-1}=f_{i+1}-f_i ]

也就是说,([j,k])中的(f_i)是一个等差数列!

凸壳

我们把所有((i,a_i))看成二维平面上一个点,则我们相当于要从左向右选择若干关键点,并在相邻关键点之间连边(包括((0,0))((n+1,0))),然后(f_i)就等于直线(x=i)与所连边交点的纵坐标。

考虑怎么选最优,于是发现最优的选法就是选出一个凸壳(比较显然),直接单调栈维护一下就好了。

代码:(O(n))

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 100000
#define LL long long
using namespace std;
int n,S[N+5];struct P
{
	LL x,y;I P(Con LL& a=0,Con LL& b=0):x(a),y(b){}
	I P operator - (Con P& o) Con {return P(x-o.x,y-o.y);}
	I LL operator ^ (Con P& o) Con {return x*o.y-y*o.x;}
}p[N+5];
int main()
{
	RI i,j,x;for(scanf("%d",&n),i=1;i<=n;++i) scanf("%d",&x),p[i]=P(i,(LL)1e5*x);//把(i,a[i])看作一个点
	RI T=1;for(p[n+1]=P(n+1,0),i=1;i<=n+1;S[++T]=i++) W(T&&((p[i]-p[S[T]])^(p[S[T]]-p[S[T-1]]))<0) --T;//单调栈求凸壳
	for(i=j=1;i<=n;++i) S[j]<i&&++j,printf("%lld
",S[j]==i?p[i].y:(p[S[j]].y*(i-S[j-1])+p[S[j-1]].y*(S[j]-i))/(S[j]-S[j-1]));//输出交点纵坐标
	return 0;
}
败得义无反顾,弱得一无是处
原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu5155.html