洛谷5155 Balance Beam

Problem

洛谷

Solution

很有意思的一道题目。

对于一个在 [ 1 , n ] [1,n] [1,n]上的点,我们有两种决策,跳下去或者继续走,我们考虑一下继续走的最大期望收益。
先说一个性质。对于当前位置 p p p,我们可以枚举在前后选定的结束点 l , r l,r l,r,设 f i f_i fi表示i走到结束点的期望收益,不难得到这样的一个方程 f i = 1 2 f i − 1 + f i + 1 f_i=\frac 1 2 f_{i-1}+f_{i+1} fi=21fi1+fi+1,而边界条件就是 f l = a l , f r = a r f_l=a_l,f_r=a_r fl=al,fr=ar
这个方程描述的是一个等差数列,所以解出来的 f x f_x fx其实是直线 ( l , a l ) (l,a_l) (l,al) ( r , a r ) (r,a_r) (r,ar) x = p x=p x=p时的取值。
那么我们就希望选择最优的两个点,使得覆盖它的线段在 x = p x=p x=p时的取值最大。

嗯…把图画一下,连上左右所有的线段,你就会发现,最后的选择其实是凸包的一部分。这正是凸包的性质!
时间复杂度 O ( n ) O(n) O(n)

Code

#include <cstdio>
using namespace std;
typedef long long ll;
const int maxn=100010;
const ll alpha=100000ll;
template <typename Tp> inline int getmin(Tp &x,Tp y){return y<x?x=y,1:0;}
template <typename Tp> inline int getmax(Tp &x,Tp y){return y>x?x=y,1:0;}
template <typename Tp> inline void read(Tp &x)
{
    x=0;int f=0;char ch=getchar();
    while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
    if(ch=='-') f=1,ch=getchar();
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    if(f) x=-x;
}
struct data{int v,id;}tmp,stk[maxn];
int n,x,top;
ll ans;
double slope(data a,data b){return (double)(b.v-a.v)/(b.id-a.id);}
int main()
{
	#ifndef ONLINE_JUDGE
	freopen("in.txt","r",stdin);
	#endif
	read(n);
	stk[++top]=(data){0,0};
	for(int i=1;i<=n;i++)
	{
		read(x);tmp=(data){x,i};
		while(top>1&&slope(stk[top-1],stk[top])<slope(stk[top],tmp)) top--;
		stk[++top]=tmp;
	}
	tmp=(data){0,n+1};
	while(top>1&&slope(stk[top-1],stk[top])<slope(stk[top],tmp)) top--;
	stk[++top]=(data){0,n+1};
	for(int i=1,j=1;i<=n;i++)
	{
		while(i>stk[j].id) ++j;
		ans=(stk[j-1].v*alpha*(stk[j].id-i)+stk[j].v*alpha*(i-stk[j-1].id));
		ans=ans/(stk[j].id-stk[j-1].id);
		printf("%lld\n",ans);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值