2016北京集训测试赛(十六)Problem A: 任务安排

Description

Solution

这道题告诉我们, 不能看着数据范围来推测正解的时间复杂度. 事实证明, 只要常数足够小, \(5 \times 10^6\)也是可以跑\(O(n \log n)\)算法的!!!
这道题有两种思路. 比较容易想到的(也是我考场上想的)一种是: 把所有任务按照权值从大到小排序, 从权值大的开始安排, 将其安排在尽可能靠后的位置; 假如位置不够, 安排不下, 则可停止. 但这样非常难统计答案, 我想到的做法是用线段树的分裂与合并来维护整个区间. 但考虑到时间复杂度以及常数大小, 还是果断放弃了.
第二种做法是考虑每个时间点在做哪个任务. 我们把所有任务按照结束时间从大到小排序, 从大到小遍历两个任务之间的事件间隔, 用优先队列来决策当前一段做哪个任务; 同时记录每个任务的完成进度, 假如当前区间把这个任务做完了, 那么在优先队列中把这个任务弹出, 接着考虑下一个任务.

#include <cstdio>
#include <algorithm>

const int N = (int)5e6;
int n, d[N], cnt[N], val[N];
int flg[(int)1e7];
struct mission
{
    int d, c, v;
    inline int operator <(const mission a) const {return v > a.v;}
}a[N];
int main()
{

    #ifndef ONLINE_JUDGE

    freopen("schedule.in", "r", stdin);
    freopen("schedule.out", "w", stdout);

    #endif

    fread(&n, 4, 1, stdin); fread(d, 4, n, stdin); fread(cnt, 4, n, stdin); fread(val, 4, n, stdin);
    // scanf("%d", &n); for(int i = 0; i < n; ++ i) scanf("%d", d + i); for(int i = 0; i < n; ++ i) scanf("%d", cnt + i); for(int i = 0; i < n; ++ i) scanf("%d", val + i);
    for(int i = 0; i < n; ++ i) a[i].d = d[i], a[i].c = cnt[i], a[i].v = val[i];
    std::sort(a, a + n);
    long long ans = 0;
    for(int i = 0; i < n; ++ i)
    {
        int cnt = 0;
        for(int j = a[i].d; cnt < a[i].c && j; -- j) if(! flg[j]) ++ cnt, flg[j] = 1;
        ans += (long long)cnt * a[i].v;
    }
    printf("%lld\n", ans);
}

posted @ 2017-08-31 20:52  Zeonfai  阅读(164)  评论(0编辑  收藏  举报