任务安排「SDOI2012」
题意
有一个数列,每个元素均有一个完成耗时\(t_i\)与完成耗费\(f_i\),每个元素的耗费等于其完成时刻乘以完成耗费。可以将这些元素分为任意组,每一组的元素完成时刻一样。每一组都会有一个固定的额外时间耗费\(s\)。数据范围\(3*10^5\)。
思路
首先推出状态转移方程\(dp[i]=min(dp[j]+s*(sumf[n]-sumf[j])+sumt[i]*(sumf[i]-sumf[j]))\)
拆开可以得到斜率
\(\frac{dp[j]-dp[k]}{sumf[j]-sumf[k]}>s+sumt[i]\)
然而斜率不具有单调性,因此考虑\(CDQ\)分治。
左侧直接更新右侧即可。
代码
#include <bits/stdc++.h>
using namespace std;
namespace StandardIO {
template<typename T>inline void read (T &x) {
x=0;T f=1;char c=getchar();
for (; c<'0'||c>'9'; c=getchar()) if (c=='-') f=-1;
for (; c>='0'&&c<='9'; c=getchar()) x=x*10+c-'0';
x*=f;
}
template<typename T>inline void write (T x) {
if (x<0) putchar('-'),x*=-1;
if (x>=10) write(x/10);
putchar(x%10+'0');
}
}
using namespace StandardIO;
namespace Project {
#define int long long
const int N=300300;
// const int INF=2147483647;
int n,s,head,tail;
int t[N],f[N],dp[N];
struct node {
int id;
int x,y;
node () {}
node (int _x,int _y) : x(_x),y(_y) {}
friend node operator + (const node &x,const node &y) {
return node(x.x+y.x,x.y+y.y);
}
friend node operator - (const node &x,const node &y) {
return node(x.x-y.x,x.y-y.y);
}
friend int operator * (const node &x,const node &y) {
return x.x*y.y-x.y*y.x;
}
} w[N],queue[N],tmp[N];
inline bool cmpid (const node &x,const node &y) {
return t[x.id]<t[y.id];
}
inline bool cmpx (const node &x,const node &y) {
return (x.x==y.x)?(x.y<y.y):(x.x<y.x);
}
inline double slope (node x,node y) {
return static_cast<double>(x.y-y.y)/static_cast<double>(x.x-y.x);
}
void merge1 (int l,int r) {
int mid=(l+r)>>1,ptr_l=l,ptr_r=mid+1;
for (register int i=l; i<=r; ++i) {
if (w[i].id<=mid) tmp[ptr_l++]=w[i];
else tmp[ptr_r++]=w[i];
}
for (register int i=l; i<=r; ++i) w[i]=tmp[i];
}
void merge2 (int l,int r) {
int mid=(l+r)>>1,ptr_l=l,ptr_r=mid+1;
for (register int i=l; i<=r; ++i) {
if (ptr_l<=mid&&(ptr_r>r||cmpx(w[ptr_l],w[ptr_r]))) tmp[ptr_l++]=w[i];
else tmp[ptr_r++]=w[i];
}
for (register int i=l; i<=r; ++i) w[i]=tmp[i];
}
void CDQ (int l,int r) {
if (l==r) return w[l].x=f[w[l].id],w[l].y=dp[w[l].id],void();
int mid=(l+r)>>1;merge1(l,r),CDQ(l,mid),head=1,tail=0;
for (register int i=l; i<=mid; ++i) {
while (head<tail&&(queue[tail]-queue[tail-1])*(w[i]-queue[tail-1])<0) --tail;
queue[++tail]=w[i];
}
for (register int i=mid+1; i<=r; ++i) {
while (head<tail&&slope(queue[head+1],queue[head])<s+t[w[i].id]) ++head;
dp[w[i].id]=min(dp[w[i].id],dp[queue[head].id]+s*(f[n]-f[queue[head].id])+t[w[i].id]*(f[w[i].id]-f[queue[head].id]));
}
CDQ(mid+1,r),merge2(l,r);
}
inline void MAIN () {
read(n),read(s);
for (register int i=1; i<=n; ++i) read(t[i]),read(f[i]),t[i]+=t[i-1],f[i]+=f[i-1],w[i].id=i;
sort(w+1,w+n+1,cmpid),memset(dp,0x3f,sizeof(dp)),dp[0]=0,CDQ(0,n);
write(dp[n]);
}
#undef int
}
int main () {
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
Project::MAIN();
}