虽然以前学过斜率优化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
时间: 2024-10-06 03:55:13