[SDOI2012]任务安排

一、题目

点此看题

二、解法

f[i]f[i]为划分到ii的最小费用,设t[i],c[i]t[i],c[i]为时间//花费前缀和,转移如下:
f[i]=f[j]+t[i]×(c[i]c[j])+s×(c[n]c[j])f[i]=f[j]+t[i]\times(c[i]-c[j])+s\times(c[n]-c[j])ss那一部分用到了费用提前计算,正常转移是O(n2)O(n^2)的,来推一波式子,设两个转移点j,kj,kj>kj>k)且jj优于kk,考虑此时的条件:
f[j]s×c[j]c[j]×t[i]<f[k]c[k]×t[i]s×c[k]f[j]-s\times c[j]-c[j]\times t[i]<f[k]-c[k]\times t[i]-s\times c[k]f[j]s×c[j]f[k]+s×c[k]c[j]c[k]<t[i]\frac{f[j]-s\times c[j]-f[k]+s\times c[k]}{c[j]-c[k]}<t[i]然后就可以用斜率优化了,但是注意到t28|t|\leq 2^8,可能为负数,前缀和不单调,所以我们不能简单用弹队首的方式来做,先维护一个斜率单调递增的图形,然后在上面二分,找到最前面满足上面式子的点,用它来转移即可。

时间复杂度O(nlogn)O(n\log n),贴个代码qwqqwq

#include <cstdio>
const int M = 300005;
#define int long long
int read()
{
    int x=0,flag=1;
    char c;
    while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
    while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
    return x*flag;
}
int n,s,hd,tl,t[M],c[M],f[M],q[M];
int up(int j,int k)
{
    return f[j]-s*c[j]-f[k]+s*c[k];
}
int down(int j,int k)
{
    return c[j]-c[k];
}
int Find(int val)
{
    int l=hd,r=tl-1,ans=-1;
    while(l<=r)
    {
        int m=(l+r)>>1;
        if(up(q[m+1],q[m])>val*down(q[m+1],q[m])) ans=m,r=m-1;
        else l=m+1;
    }
    if(ans==-1) return q[tl];
    return q[ans];
}
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();
    }
    for(int i=1;i<=n;i++)
    {
        int j=Find(t[i]);
        f[i]=f[j]+(c[i]-c[j])*t[i]+s*(c[n]-c[j]);
        while(hd<tl && up(q[tl],q[tl-1])*down(i,q[tl])>=up(i,q[tl])*down(q[tl],q[tl-1])) tl--;
        q[++tl]=i;
    }
    printf("%lld\n",f[n]);
}
发布了367 篇原创文章 · 获赞 14 · 访问量 1万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 技术黑板 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览