二刷提高课,题解目录在这里— 提高课的题解目录
此题开始用上了斜率优化,发现这个和凸包有关(没学)
但是不影响理解,首先要将我们上题推出的式子变形一下
我们可以发现,我们所枚举的是j,而所有与j有关的点,有两个我们设置为x,y
我们可以发现我们所要求的最小的f[i]其实就是最小的截距
我们可以通过画几条线了解一下我们所需要找的点(使得截距最小)
一定在最外面,也就是在凸包上(边界点),因为内部的点会将其上移所以会将截距变大
那么我们如何处理呢?
每次枚举斜率是不变的,我们想找到最外面的点其实就是找到斜率第一次大于此斜率的点,并且斜率是单调递增的
那么我们在查询之前将小于此斜率的删掉不久可以了吗!
随后当我们插入时,很有可能出现原本在凸包上现在加上这个点不在凸包上了
通过画图可以知道,当加入3若想删去2,只有12斜率大于13,我们将2删去
可以这样做的最核心的原因是因为c是大于零,x轴(c的前缀和)一定是逐渐向右靠的即横坐标是单调递增的
注意我们两次的斜率算法是不同的,查询的时候我们用的是公式里的斜率
而当我们插入时,此时x,y已经算出,所以斜率是此点和前面点构成的斜率
虽然没学过凸包这里我们也了解了如何删去那些不是凸包上的点了
首先将x进行排序,随后一一看其斜率,如果出现上面分析的情况就将其删去
不过我们算凸包的时候是不会将前面那些斜率小的删去的(算的是凸包为什么要删小斜率)
这样是算的下面的凸包,上面应该同理吧(不了解)
那么接下来就是单调队列问题了
#include<iostream>
#include<cstring>
using namespace std;
const int N=3e5+10;
long long sc[N],st[N],f[N],q[N];
int n,s;
int main()
{
cin>>n>>s;
for(int i=1;i<=n;i++)
{
cin>>st[i]>>sc[i];
st[i]+=st[i-1];
sc[i]+=sc[i-1];
}
int hh=0,tt=0;
q[0]=0;
for(int i=1;i<=n;i++)
{
//以公式的斜率进行查询
while(hh<tt&&f[q[hh+1]]-f[q[hh]]<=(st[i]+s)*(sc[q[hh+1]]-sc[q[hh]]))hh++;
int j=q[hh];
f[i]=f[j]-(st[i]+s)*sc[j]+st[i]*sc[i]+s*sc[n];
//点已经算出来了,且x是逐渐向右靠的,所以我们可以通过判断斜率大小的方法删去那些不在凸包上的点
while(hh<tt&&(f[q[tt]]-f[q[tt-1]])*(sc[i]-sc[q[tt-1]])>=(f[i]-f[q[tt-1]])*(sc[q[tt]]-sc[q[tt-1]]))tt--;
q[++tt]=i;
}
cout<<f[n];
return 0;
}