斜率优化
斜率优化
斜率优化在判断线段左右关系时应善用叉积.
// b 在 a 的逆时针为正,夹角为 a 转到 b 的有向角度(sin) double cross(Vector a, Vector b) { return a.x * b.y - a.y * b.x; }
通常会把题目化简成求最小截距,然后求最小截距具有一定的单调性故可以维护凸包.
同时,也可以看成是若干个一次函数求固定某个横坐标时的最小交点,可以用李超线段树来维护.
摆渡车
来源:洛谷P5017
斜率优化做法:
#include#define N 6000009 #define ll long long #define setIO(s) freopen(s".in","r",stdin) using namespace std; ll dp[N], sum[N]; int num[N], q[N], head, tail, n, m; ll cross(int a, int b, int c, int d) { // (a, b) and (c, d) return 1ll * a * d - b * c; } ll Y(int x) { return sum[x] + dp[x]; } ll X(int x) { return num[x]; } int main() { // setIO("input"); scanf("%d%d", &n, &m); int mx = 0; for(int i = 1; i <= n ; ++ i) { int x; scanf("%d", &x); num[x] ++ , sum[x] += 1ll * x; mx = max(mx, x); } for(int i = 1; i <= mx + m; ++ i) { sum[i] += sum[i - 1]; num[i] += num[i - 1]; } head = 1, tail = 0; for(int i = 0; i <= mx + m ; ++ i) { // 当前的斜率要求. if(i - m >= 0) { // i - m 进来. while(head < tail&&cross(X(q[tail])-X(i-m),Y(q[tail])-Y(i-m),X(q[tail-1])-X(i-m),Y(q[tail-1])-Y(i-m))>=0) -- tail; q[++ tail] = i - m; } while(head < tail && Y(q[head + 1])-Y(q[head])<1ll*i*(X(q[head + 1])-X(q[head]))) ++ head; if(i < m) { dp[i]=1ll*num[i]*i-sum[i]; } else { dp[i]=1ll*num[i]*i-sum[i]+Y(q[head])-1ll*i*num[q[head]]; } } ll ans = 1000000000000ll; for(int i = mx; i <= mx + m ; ++ i) ans = min(ans, dp[i]); printf("%lld", ans); return 0; }