[LOJ10186]任务安排

link

试题分析

一道斜率优化的dp

易得方程$f[j]=(S+t[i])\times c[j]+f[i]-t[i]\times c[i]+s\times c[n]$,为什么要写成这要,因为这样其实就可以将$min$拆掉,并且是一个函数形式,$(S+t[i])$为斜率,$f[i]-t[i]\times c[i]+s\times c[n]$为截距。

并且当我们要决策$i$用哪一个j时,我们会发现斜率是一样的,就只有截距会不同。所以我们要让截距越短。

所以发现当斜率为上升时,即为下凸壳的形式,所以我们可以用单调队列去优化

但是$t$有可能是负数,我们将线进行平移的时候要保证此点之前的斜率都小于$k$,之后全大于$k$,所以需要二分找到此区间

20181216 update:

为什么当截距最短是会取到$min{f[i]}$,因为$f[i]-t[i]\times c[i]+s\times c[n]$这个式子中$-t[i]\times c[i]+s\times c[n]$是定值,而又因为$f[i]$是$+f[i]$,所以只需要维护最小值即可。

但当截距出现$-f[i]$时,就必须让截距最大了,要维护的是一个上凸壳。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<climits>
#define int long long
using namespace std;
inline int read(){
    int f=1,ans=0;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();}
    return f*ans;
}
const int N=300001;
int l,r,num[N];
int f[N],t[N],c[N],S,n,que[N];
int query(int pos,int X){
    int L=l,R=r,maxn=0;
    int mid;
    while(L<=R){
        mid=L+R>>1;
        if(f[que[mid+1]]-f[que[mid]]<=X*(c[que[mid+1]]-c[que[mid]])) L=mid+1,maxn=max(maxn,mid);
        else R=mid-1;
    }
    return que[maxn+1];
}
signed main(){
    n=read(),S=read();
    for(int i=1;i<=n;i++) t[i]=t[i-1]+read(),c[i]=c[i-1]+read();
    l=1,r=1;f[0]=0;
    for(int i=1;i<=n;i++){
        int H=query(i,S+t[i]);
        f[i]=f[H]-(S+t[i])*c[H]+t[i]*c[i]+S*c[n];
        while(l<r&&(f[que[r]]-f[que[r-1]])*(c[i]-c[que[r]])>=(f[i]-f[que[r]])*(c[que[r]]-c[que[r-1]])) r--;
        que[++r]=i;
    }printf("%lld\n",f[n]);
    return 0;
}
View Code

 

posted @ 2018-12-12 15:07  siruiyang_sry  阅读(186)  评论(0编辑  收藏  举报