题解 P5155 【[USACO18DEC]Balance Beam】
你真的不想点进来看一看吗
(翻了翻其他的题解,觉得它们没讲清楚为什么一定选择两个点作为决策结束点)
我相信很多通过了这道题的人都没有弄清楚这个策略的正确性
Problem
题意概要:给定一个长为
Solution
关键在于需要考虑当前是选择移动还是直接结束。一个很明了的观点:如果当前移动后的收益期望比当前位置的收益大,那么会选择移动;否则选择直接停止。直接停止的贡献已经知道,那么要求的就是当前点选择移动操作后的收益期望
有一个结论:在长度为
关于这个结论的证明,考虑设在
这个结论的用处后面再说
假设我们已知一些节点移动的期望收益比当前点停止的收益低,即如果进行随机游走,一旦到达这些点,一个极端聪明的人是绝不会继续移动的,设这些点为停止点
会发现,如果从
不妨设为
现在我们得到了一个优秀的做法,就是对于每个点,找到其前后的第一个停止点,得到其移动的期望收益然后与自己的停止收益取最大值
但如何求出所有的停止点呢?
进一步,画个图可知,设
不难发现若将所有停止点
所以为了得到所有的停止点,只要对所有的
(要看图的兄弟可以看楼下的,逃)
证明真有趣
Code
#include <bits/stdc++.h>
typedef long long ll;
inline void read(ll&x){
char c11=getchar();x=0;while(!isdigit(c11))c11=getchar();
while(isdigit(c11))x=x*10+c11-'0',c11=getchar();
}
const int N=101000,bs=1e5;
int n,tp;
struct vec{
int x;ll y;
inline vec(){}
inline vec(const int&X,const ll&Y):x(X),y(Y){}
friend inline vec operator - (const vec&A,const vec&B) {return vec(A.x-B.x,A.y-B.y);}
friend inline ll operator * (const vec&A,const vec&B) {return A.x*B.y-A.y*B.x;}
}p,st[N];
void push(vec p){
while(tp&&(p-st[tp])*(st[tp]-st[tp-1])<=0)--tp;
st[++tp]=p;
}
int main(){
scanf("%d",&n);vec p;
for(int i=1;i<=n;++i)p.x=i,read(p.y),p.y*=bs,push(p);
push(vec(n+1,0));
for(int i=1,j=0;i<=n;++i){
while(j<tp&&st[j].x<i)++j;
if(st[j].x==i)printf("%lld\n",st[j].y);
else printf("%lld\n",((st[j].x-i)*st[j-1].y+(i-st[j-1].x)*st[j].y)/(st[j].x-st[j-1].x));
}return 0;
}