[USACO18DEC]Balance Beam

传送门

分步证明,首先抛出结论:每个点的策略要么是不动,要么是随机移动直到左右两个点中的一个落下。

结论1:

从点x开始在a和b之间移动在b落下的概率为 (x-a)/(b-a)
设概率为f[x]=k,则f[a]=0,f[b]=1。因为x有1/2几率往左走或往右走,所以
f[x]=(f[x-1]+f[x+1])/2
即f(x)满足等差数列的性质,则图像为由(a,0),(b,1)组成的直线,做两条垂线做相似三角形即可。从b到a反过来即可。

结论2:

ans(x)=max(val[x],(val[a]×(b-x)/(b-a)+val[b]×(x-a)/(b-a))/2)
期望的基本概念,贡献×概率的平均数

结论3:

若最优策略为使一个点在两个停止点之间移动则停止点的连线为一个凸包
若一个在凸包内的点为停止点,则在该停止点的期望计算为它本身的贡献值,但实际上若以原本就在它两侧的停止点计算贡献使贡献更高(凸包的定义)

结论4:

最优策略就是在两个停止点之间移动
无论是什么策略,在本题中必然有停止下来的点,而且必然有两个以上。
若只有一个停止点,可实际上到达0或n+1时点必定会停下来,故至少有两个停止点
若有大于两个的停止点,则在到达一个停止点(非边界处)可以选择停或不停,若不停相当于这个点不是停止点,若停就相当于这个点就是停止点,无法再延伸。因为在本题中无论你何时到达x点,你的状态都是相同的,不存在根据不同的状态来选则的策略,故不存在大于两个的停止点(会不会有点绕)

#include <cstdio>
#include <queue>
#include <vector>
#include <iostream>
using namespace std;
const int maxn = 1e5+5;
typedef long long LL;
int readint()
{
	int x=0,f=1;char s=getchar();
	#define sc (s=getchar())
	while(s<'0'||s>'9'){if(s=='-')f=-1;sc;}
	while(s>='0'&&s<='9'){x=(x<<3)+(x<<1)+(s^48);sc;}
	#undef sc
	return x*f;
}
void writeint(int x)
{
	if(x>9)
		writeint(x/10);
	putchar((x%10)^48);
}
int q[maxn],top;
LL a[maxn];
LL ans[maxn];
bool pd(int x,int y,int z)
{
	return (y-x)*(a[z]-a[x])-(z-x)*(a[y]-a[x])>=0;
}
int main ()
{
	//writeint(readint());
	//freopen("balance.in","r",stdin);
	//freopen("balance.out","w",stdout);
	int n=readint();
	for(int i=1;i<=n;i++)
		a[i]=readint();
	q[++top]=0;
	for(int i=1;i<=n+1;i++)
	{
		while(top>=2&&pd(q[top-1],q[top],i))
			top--;
		q[++top]=i;
	}
	for(int i=2;i<=top;i++)
	{
		int l=q[i-1],r=q[i];
		for(int j=l;j<=r;j++)
			ans[j]=1e5*(a[l]*(r-j)+a[r]*(j-l))/(r-l);
	}
	for(int i=1;i<=n;i++)
		cout<<ans[i]<<endl;
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值