Chlience

【题解】BZOJ 2726 [SDOI2012]任务安排
Problem传送门 >ω<题目大意:按顺序给定 $n$ 个子任务,每个任务用时 $t_i$ ,费用系数 $f_i...
扫描右侧二维码阅读全文
13
2018/10

【题解】BZOJ 2726 [SDOI2012]任务安排

Problem

传送门 >ω<

题目大意:
按顺序给定 $n$ 个子任务,每个任务用时 $t_i$ ,费用系数 $f_i$
连续的多个(一个)子任务合成为大任务,大任务的用时和费用系数为所有子任务之和,启动一个大任务需要时间 $S$ ,每次进行一个大任务,其费用为 结束时间 * 费用系数 ,问最小费用为多少(大任务也要按照先后顺序进行)

要求所有子任务都被包括在大任务之中

题面复杂得一匹...

Solution

因为根据习惯我们用 $f$ 来进行动态规划,所以将 $g$ $f$ 互换一下

先从比较简单的开始
设 $sumg[i]$ 为 $g[i]$ 前缀和
设 $sumt[i]$ 为 $r[i]$ 前缀和
设 $f[i]$ 为 将前 $i$ 个小任务分成若干个大任务所需要的最小费用
我们先不考虑每次机器启动的 $S$ ,易得转移

$$f[i] = min(f[j] , sumt[i] * (sumg[i] - sumg[j]))$$

经过观察发现,每次加入一个 $S$ 都会对后面每个点增加一个贡献 $s * g[k] (k > j)$ ,即 $s * sumg[n] - sumg[j]$

所以转移方程化为

$$f[i] = min(f[j] + sumt[i] * (sumg[i] - sumg[j]) + s * (sumg[n] - sumg[j]))$$

显然这样就能使用 $O(n^2)$ 的算法 通过 $60\%$ 的数据

那么如何进一步降低复杂度呢?

假设现在 $i$ 状态从 $k$ 状态转移比 $j$ 状态要优,则有

$$f[j] + sumt[i] * (sumg[i] - sumg[j]) + s * (sumg[n] - sumg[j])) > f[k] + sumt[i] * (sumg[i] - sumg[k]) + s * (sumg[n] - sumg[k]))$$

化简可得

$$f[j] - s * sumg[j] - sumt[i] * sumg[j] > f[k] - s * sumg[k] - sumt[i] * sumg[k]$$

$$\frac{f[k] - f[j]}{sumg[k] - sumg[j]} < s + sumt[i]$$

其实这就已经可以做了,显然需要维护一个下凸包,在插入的时候弹出无用状态

但是后面的斜率 $s + sumt[i]$ 没有单调性,所以说就不能够从状态头部弹出节点,不能保证头部状态最优,所以在可能的状态序列中二分查找,直到右侧节点不优于当前节点为止

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1000010;
#define mid ((l + r) >> 1)
ll t[N] , g[N];
ll f[N];
ll sta[N] , top;
ll n , s;
ll read() {
    ll ans = 0 , flag = 1;
    char ch = getchar();
    while(ch > '9' || ch < '0') {if(ch == '-') flag = - 1; ch = getchar();}
    while(ch >= '0' && ch <= '9') {ans = ans * 10 + ch - '0'; ch = getchar();}
    return ans * flag;
}
bool check(int x , int i) {
    if(x == top) return 0;
    if(f[sta[x + 1]] - f[sta[x]] <= (g[sta[x + 1]] - g[sta[x]]) * (s + t[i])) return 1;
    return 0;
}
bool slope(int x , int y , int z) {
    return (f[x] - f[y]) * (g[y] - g[z]) <= (f[y] - f[z]) * (g[x] - g[y]);
}
int work(int i) {
    int l = 1 , r = top;
    if(l == r) return sta[l];
    while(l < r) {
        if(check(mid , i)) l = mid + 1;
        else r = mid;
    }
    return sta[l];
}
int main() {
    n = read(); s = read();
    for(int i = 1 ; i <= n ; ++ i) {
        t[i] = read(); t[i] += t[i - 1];
        g[i] = read(); g[i] += g[i - 1];
    }
    sta[++ top] = 0;
    for(int i = 1 ; i <= n ; ++ i) {
        int j = work(i);
        f[i] = f[j] + t[i] * (g[i] - g[j]) + s * (g[n] - g[j]);
        while(top >= 2) {
            if(slope(i , sta[top] , sta[top - 1])) sta[top --] = 0;
            else break;
        }
        sta[++ top] = i;
    }
    printf("%lld\n" , f[n]);
    return 0;
}
Last modification:October 13th, 2018 at 10:48 pm

Leave a Comment