把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

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

点此看题面

  • 给定一个长度为\(n\)的序列\(a\)
  • 当你处于某个位置\(i\)的时候,你可以选择结束游戏并获得\(a_i\)的报酬,也可以不结束游戏随机走向\(i-1\)\(i+1\),如果走到\(0\)\(n+1\)则只能结束游戏且无法获得任何报酬。
  • 对于每个起始位置,求出最优策略下的期望报酬。
  • \(n\le10^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 i\in(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\n",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;
}
posted @ 2021-05-31 08:57  TheLostWeak  阅读(57)  评论(0编辑  收藏  举报