[SDOI2012]任务安排

虽然以前学过斜率优化dp但是忘得和没学过一样了。就当是重新学了。

题意很简单(反人类),利用费用提前的思想,考虑这一次决策对当前以及对未来的贡献,设 (f_i) 为做完前 (i) 个任务的贡献,(t_i) 为时间前缀和, (c_i) 为费用前缀和,容易得到

[f_i = Min_{0 leq j < i} (f_j + t_i (c_i - c_j) + s (c_n - c_j) ]

直接暴力转移,时间复杂度 (O(n^2))

考虑斜率优化,将转移关系变形为

[f_j = (s+t_i) c_j + f_i - t_i c_i - s c_n ]

(y=f_j, x=c_j) ,则转化为标准线性形式 (y=Ax+B)

我们要最小化 (f_i) 就要最小化 (B)

如果原题中每个任务的完成时间都为正数,那么转移时 (A) 是单调增的,可以用单调队列维护。

这里任务的完成时间可以为负(很反人类嘛),在单调栈上二分即可。

细节手推公式 + 小心写代码。

#include <bits/stdc++.h>
using namespace std;

#define int long long
const int N = 300005;

int n,s,t[N],c[N],f[N],l,r,q[N];

int binsearch(int l,int r,int i) {
    while(l<r) {
        int mid = (l+r)/2;
        if((f[q[mid+1]]-f[q[mid]]) <= (s+t[i]) * (c[q[mid+1]]-c[q[mid]]))
            l = mid+1;
        else
            r = mid;
    }
    return l;
}

signed main() {
    ios::sync_with_stdio(false);
    cin>>n>>s;
    for(int i=1;i<=n;i++) cin>>t[i]>>c[i];
    for(int i=1;i<=n;i++) t[i]+=t[i-1];
    for(int i=1;i<=n;i++) c[i]+=c[i-1];
    l=1; r=1;
    memset(f, 0x3f, sizeof f);
    f[0]=0;
    for(int i=1;i<=n;i++) {
        int j = binsearch(l,r,i);
        f[i] = f[q[j]] + t[i]*(c[i]-c[q[j]]) + s*(c[n]-c[q[j]]);
        while(l<r && (f[q[r]]-f[i])*(c[q[r]]-c[q[r-1]]) >= (c[q[r]]-c[i])*(f[q[r]]-f[q[r-1]])) r--;
        q[++r] = i;
    }
    cout<<f[n]<<endl;
}
原文地址:https://www.cnblogs.com/mollnn/p/12243060.html