斜率优化
感谢全机房唯一女装大佬 little-uu 的讲解
对于斜率优化,妙极秒极,所以我是没办法讲 /摊手
对于类似于 \(f[i] = f[j] + a[i] \times b[j] + \dots\) 之类的式子可以使用斜率优化
首先想办法将式子里的变量的下表全变成 i 或 j ,然后将 \(a[i] \times b[j]\) 形式的式子放到一边,只和 i 有关的式子也放到这一边,只跟 j 有关的式子放到另一边。
我们得到类似于 \(a[i] \times b[j] + f[i] + \dots = f[j] + \dots\) 的式子。
我们考虑一次函数 \(y = kx + b\)
将其中 \(a[i]\) 视为 \(k\) ,\(b[j]\) 视为 \(x\) , \(f[i] + \dots\) 视为 \(b\) , \(f[j] + \dots\) 视为 \(y\)
我们可以得到对于此直线,它经过 \(\left(b[j],f[j]+\dots \right)\) ,斜率为 \(a[i]\) ,\(f[i]\) 只跟截距有关。
对于已经处理好的 \(f[j]\) 来说,都可以确定一个点,而对于需要计算的 \(f[i]\) 来说,都可以确定这条直线的斜率,两者结合可以求出一次函数截距。于是我们可以将问题转化。
我们现在有一些点,需要 求出在特定斜率下的截距最值,可以知道可以对答案造成贡献的只能是凸包上的点,使用栈来维护凸包。对于 \(a[i]\) 单调增的题目来说,可以使用双端队列来进行均摊 \(O(1)\) 的转移,否则需要在凸包上二分。
#include <cstdio>
#include <cstring>
#include <algorithm>
#define j1 q[l]
#define j2 q[l+1]
#define int long long
using namespace std;
int read()
{
int a = 0,x = 1;char ch = getchar();
while(ch > '9' || ch < '0') {if(ch == '-') x = -1;ch = getchar();}
while(ch >= '0' && ch <= '9') {a = a*10 + ch-'0';ch = getchar();}
return a*x;
}
const int N=1e6+7;
int n,x[N],p[N],c[N];
int a[N],b[N],A[N],f[N];
int q[N],l=1,r=1;
inline bool check(int a1,int a2,int a3)
{
return (b[a3]+f[a3]-b[a1]-f[a1])*(a[a3]-a[a2]) > (b[a3]+f[a3]-b[a2]-f[a2])*(a[a3]-a[a1]);
}
signed main()
{
n = read();
for(int i = 1;i <= n;i ++) {
x[i] = read(),p[i] = read(),c[i] = read();
a[i] = a[i-1]+p[i],b[i] = b[i-1]+x[i]*p[i];
A[i] = x[i]*a[i];
}
for(int i = 1;i <= n;i ++) {
while(l<r) {
if((b[j1]+f[j1] - b[j2]-f[j2]) > x[i]*(a[j1]-a[j2])) l ++;
else break;
}
#define j q[l]
f[i] = f[j]+A[i]-x[i]*a[j]-b[i]+b[j]+c[i];
while(l<r) {
if(check(q[r-1],q[r],i)) r --;
else break;
}
q[++r] = i;
}
printf("%lld",f[n]);
return 0;
}