123789456ye

已AFO

斜率优化总结

斜率优化dp
考虑一类\(dp\)方程

\[dp[i]=\min_{L(i)\le j\le R(i)}{f(j)+val(i,j)} \]

\(min\)改成\(max\)也是可以的
其中\(val\)中含有\(ij\)乘积这一项
例题:任务安排
转移方程

\[dp[i]=\min_{0\le j<i}\{dp[j]+sumt[i]*(sumf[i]-sumf[j])+s*(sumf[n]-sumf[j])\} \]

把式子拆开,把\(dp[j]\)放到左边,\(dp[i]\)放到右边作为截距中的一项

\[dp[j]=(s+sumt[i])*sumf[j]+(dp[i]-sumt[i]*sumf[i]-s*sumf[n]) \]

对于每一个确定的\(i\),截距都是\(dp[i]+C\),其中\(C\)只与\(i\)有关
所以我们要最小化截距,于是我们维护一个点集为\((sumf[i],dp[i])\)的下凸包
由于这道题斜率一定,所以我们相当于是用一条斜率为\(s+sumt[i]\)的直线去切这个凸包
所以我们找到\(k\le s+sumt[i]\)\(k\)最大的点即可,这个\(k\)是表示这一个点和前一个点之间的斜率(具体可以画个图)
维护凸包,并且横坐标递增,斜率递增,直接上单调队列即可

#include<bits/stdc++.h>
using namespace std;
inline void read(int& x)
{
	x = 0; char c = getchar();
	while (!isdigit(c)) c = getchar();
	while (isdigit(c)) x = x * 10 + c - '0', c = getchar();
}
#define maxn 5005
int t[maxn], f[maxn], q[maxn], dp[maxn], n, s;
inline int dy(int x, int y) { return dp[y] - dp[x]; }
inline int dx(int x, int y) { return f[y] - f[x]; }
int main()
{
	read(n), read(s);
	for (int i = 1; i <= n; ++i) read(t[i]), read(f[i]), t[i] += t[i - 1], f[i] += f[i - 1];
	for (int l = 1, r = 1, i = 1; i <= n; ++i)
	{
		while (l < r && dy(q[l], q[l + 1]) <= 1ll * (s + t[i]) * dx(q[l], q[l + 1])) ++l;
		dp[i] = dp[q[l]] - (s + t[i]) * f[q[l]] + t[i] * f[i] + s * f[n];
		while (l < r && 1ll * dx(q[r - 1], q[r]) * dy(q[r], i) < 1ll * dx(q[r], i) * dy(q[r - 1], q[r])) --r;
		q[++r] = i;
	}
	printf("%d\n", dp[n]);
	return 0;
}

以及如果两个都不单调,就要上平衡树/cdq,比如这个

posted @ 2020-04-06 15:07  123789456ye  阅读(119)  评论(0编辑  收藏  举报