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=21fi−1+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;
}