测试地址:Lightning Conductor
题目大意:给定一个数列,对每个求一个最小的非负整数,使得对于所有,。
做法:本题需要用到决策单调性优化DP。
今天终于学习了真·决策单调性优化DP,比较开心。这种优化的优化对象通常是我们所说的1D/1D状态转移方程,即形如:
或:
这类状态转移,若满足某些性质,我们就能用决策单调性优化DP。其实和都可以,这里先讨论,同理。
这个性质是:
一般我们认为这个条件是满足四边形不等式的充要条件,但这里主要不是考虑四边形不等式的问题。观察上面的式子,我们发现实际上满足的是,在增大时,同样的到的变化率不断变小。这就意味着,一但存在一个时刻,使得对于,,那么就没用了,因为增长总是比增长快。于是我们证明出了决策单调性,即对于,使得最小的满足。
有了决策单调性,首先想到的就是缩小枚举范围。然而在没有其他限制条件的情况下,直接枚举最坏的复杂度仍然是,并没有起到任何实质上的优化。于是我们反过来考虑,不是考虑“使得最小的决策变量是多少”,而是考虑”一个决策变量能使得哪些最小”。我们需要维护一个队列,队列里每一项都是一个区间,表示某个决策变量最优的区间。当我们加入一个新的决策变量时,在队列中从后往前判断,如果新增决策变量在这个区间的左端点上已经比之前的决策变量优了,我们就把当前区间删掉,继续向前判断。当我们停住的时候,显然决策变量的更改点在区间的内部,因为一系列的优秀性质所以我们可以二分,于是对应地更新即可。
因此,决策单调性优化DP的时间复杂度是的。
说了这么多,回到本题。移项后得到,于是我们需要求出右边的最大值,这样就得到了上面方程的第二种形式。我们显然可以求出每个点左边和右边的决策点,最后取个即可。我们可以用类似的思路证明决策单调性(注意上面所有式子的符号在此时应该相反,因为变成了),然后使用上面的方法优化即可。时间复杂度为。
以下是本人代码:
#include <bits/stdc++.h>
using namespace std;
int n,q[500010],l[500010],r[500010];
double a[500010],f[500010],g[500010];
double calc(int l,int r)
{
return a[l]+sqrt(abs(r-l))-a[r];
}
int solve(int k,int l,int r,int i)
{
while(l<r)
{
int mid=(l+r)>>1;
if (calc(k,mid)>calc(i,mid))
l=mid+1;
else r=mid;
}
return l;
}
void dp(double *f)
{
int h=1,t=0;
for(int i=1;i<=n;i++)
{
l[h]++;
if (h<=t&&r[h]<l[h]) h++;
if (h>t||calc(i,n)>calc(q[t],n))
{
while(h<=t&&calc(i,l[t])>calc(q[t],l[t])) t--;
if (h>t) q[++t]=i,l[t]=i,r[t]=n;
else
{
int tmp=solve(q[t],l[t],n,i);
r[t]=tmp-1;
q[++t]=i,l[t]=tmp,r[t]=n;
}
}
f[i]=calc(q[h],i);
}
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%lf",&a[i]);
dp(f);
for(int i=1;i<n-i+1;i++)
swap(a[i],a[n-i+1]);
dp(g);
for(int i=1;i<=n;i++)
printf("%d
",max(0,(int)ceil(max(f[i],g[n-i+1]))));
return 0;
}