斜率优化


斜率优化

斜率优化在判断线段左右关系时应善用叉积.  

// 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; 
}