题意:有n个人,每个人有一件衣服需要洗,可以自己手洗花费t时间,也可以用洗衣机洗,但是洗衣机只有一台,即每个时刻最多只能有·一个人用洗衣机洗衣服。现在给你每个人最早可以开始洗衣服的时间,问当洗衣机的洗衣时间分别为1, 2....t的时候洗完所有衣服的最短时间。

思路:首先容易想到我们先把所有人按照洗衣服的时间排序。我们发现,最终洗衣服时间的瓶颈肯定在于后面的人,所以我们考虑怎么使用洗衣机使得总的洗衣时间最短。首先最后一个人是一定要用洗衣机洗衣服的,因为洗衣机的洗衣服时间 <= t, 那么最后一个人用洗衣机洗衣服不会让总时间变长,只可能变短。同理,如果最后一个人用洗衣机和倒数第二个人不冲突的话,倒数第二个人肯定也用洗衣机了。如果倒数第二个人用不了洗衣机,那么前面的人也没必要用洗衣机了,因为倒数第二个人的洗衣时间肯定大于等于前面的人,最后时间的瓶颈只可能在倒数第二个人身上或者倒数第一个人身上了。以此类推,我们可以发现。最后用洗衣机的人的序列肯定是原序列的一个后缀。那么,对于当前的x,怎么确定哪些人用洗衣机呢?设b[i]为第i个人开始洗衣服的时间,假设是从第i个人开始用洗衣机洗衣服,时间限制可能是两种:1:来自第i - 1个人手洗的时间限制:b[i - 1] + t。2:后面的人用洗衣机的时间的结束时间。这个结束时间怎么算呢?我们假设从第i个人开始用洗衣机,最理想情况是他一用完洗衣机别人就可以用,那么时间是b[i] + (n - i + 1) * x, 我们把i及其以后的所有人的理想状态时间都算出来,取最大值就是实际的结束时间。那么我们发现从i开始的人用洗衣机的时间是max(b[i - 1] + t, max(b[k] + (n - k + 1) * x)) (k >= i)。容易发现,b[k] + t是个单调递增的函数, max(b[k] + (n - k + 1) * x)是个单调递减的函数,他们俩取max是一个单谷函数,最优解就是谷底。这个好像不是严格单调函数不能三分,只能暴力枚举。那么对于一个特定的x时间复杂度是O(n)的。但是现在有n个x,怎么办?我们发现,当x等于t的时候,谷底一定在最后面,随着x的不断变小,谷底会逐渐往前移动,那么我们可以用一个指针来指向谷底,每当x减一的时候判断一下谷底是不是前移了,如果是就往前移动。那么新的问题出现了,b[i - 1] + t是常数不用维护,x减一之后max(b[k] + (n - k + 1) * x))好像得全部重新计算了?如果把n - k + 1看成斜率,b[k]看成截距,那么问题就转化为了在一堆直线中当横坐标确定时找纵坐标的最大值,这个用李超线段树可以在O(log(n))的时间内做到。

代码:

#include <bits/stdc++.h>
#define LL long long
#define INF 0x3f3f3f3f
#define ls (o << 1)
#define rs (o << 1 | 1)
using namespace std;
const int maxn = 1000010;
struct line {
double k, b;
};
line a[maxn];
int tot;
struct node {
int id;
};
node tr[maxn * 4];
double get_pos(int x, int y) {
return a[x].k * y + a[x].b;
}
double cross(int x, int y) {
return (a[x].b - a[y].b) / (a[y].k - a[x].k);
}
void init(int o, int l, int r) {
tr[o].id = 0;
if(l == r) return;
int mid = (l + r) >> 1;
init(ls, l, mid);
init(rs, mid + 1, r);
}
void update(int o, int l, int r, int now) {
if(!tr[o].id) {
tr[o].id = now;
return;
}
int x = tr[o].id;
double l1 = get_pos(now, l), r1 = get_pos(now, r);
double l2 = get_pos(x, l), r2 = get_pos(x, r);
if(l1 <= l2 && r1 <= r2) {
return;
}
else if(l1 > l2 && r1 > r2) {
tr[o].id = now;
return;
}
int mid = (l + r) >> 1;
double y = cross(now, x);
if(y <= mid) update(ls, l, mid, r1 > r2 ? x : now);
else update(rs, mid + 1, r, l1 > l2 ? x : now);
if((y <= mid && r1 > r2) || (y > mid && l1 > l2))
tr[o].id = now;
}
int query(int o, int l, int r, int pos) {
if(l == r) {
return tr[o].id;
}
int mid = (l + r) >> 1, ans = 0;
if(pos <= mid) ans = query(ls, l, mid, pos);
else ans = query(rs, mid + 1, r, pos);
if(!tr[o].id) return ans;
int x = tr[o].id;
if(get_pos(x, pos) > get_pos(ans, pos)) return x;
else return ans;
}
int b[maxn];
LL res[maxn];
int main() {
int n;
LL t;
while(~scanf("%d%lld", &n, &t)) {
init(1, 1, t);
for (int i = 1; i <= n; i++) {
scanf("%d", &b[i]);
}
sort(b + 1, b + 1 + n);
for (int i = 1; i <= n; i++) {
a[i] = (line){(double)n - i + 1, (double)b[i]};
}
b[0] = -t;
LL tmp, tmp1;
update(1, 1, t, n);
int p = n - 1;
for (int i = t; i >= 1; i--) {
while(1) {
int ans = query(1, 1, t, i);
LL t1 = b[ans] + ((LL)n - ans + 1) * i;
tmp = max(t1, b[p] + t);
if(p == 0) break;
tmp1 = max(max(t1, b[p] + ((LL) n - p + 1) * i), b[p - 1] + t);
if(tmp1 <= tmp) {
update(1, 1, t, p);
p--;
}
else break;
}
res[i] = tmp;
}
for (int i = 1; i < t; i++) {
printf("%lld ", res[i]);
}
printf("%lld\n", res[t]);
}
}

  

2019 ICPC南京网络预选赛 I Washing clothes 李超线段树的更多相关文章

  1. 南京网络赛G-Lpl and Energy【线段树】

    During tea-drinking, princess, amongst other things, asked why has such a good-natured and cute Drag ...

  2. 2019 ICPC南京网络赛 F题 Greedy Sequence(贪心+递推)

    计蒜客题目链接:https://nanti.jisuanke.com/t/41303 题目:给你一个序列a,你可以从其中选取元素,构建n个串,每个串的长度为n,构造的si串要满足以下条件, 1. si ...

  3. 2019 ICPC 南京网络赛 F Greedy Sequence

    You're given a permutation aa of length nn (1 \le n \le 10^51≤n≤105). For each i \in [1,n]i∈[1,n], c ...

  4. 2019 ICPC 南京网络赛 H-Holy Grail

    As the current heir of a wizarding family with a long history,unfortunately, you find yourself force ...

  5. 2019南昌邀请赛网络预选赛 M. Subsequence

    传送门 题意: 给出一个只包含小写字母的串 s 和n 个串t,判断t[i]是否为串 s 的子序列: 如果是,输出"YES",反之,输出"NO": 坑点: 二分一 ...

  6. 2019 ICPC 南昌网络赛

    2019 ICPC 南昌网络赛 比赛时间:2019.9.8 比赛链接:The 2019 Asia Nanchang First Round Online Programming Contest 总结 ...

  7. ACM学习历程—SNNUOJ 1110 传输网络((并查集 && 离线) || (线段树 && 时间戳))(2015陕西省大学生程序设计竞赛D题)

    Description Byteland国家的网络单向传输系统可以被看成是以首都 Bytetown为中心的有向树,一开始只有Bytetown建有基站,所有其他城市的信号都是从Bytetown传输过来的 ...

  8. 2019 ICPC南京站网络赛 H题 Holy Grail(BF算法最短路)

    计蒜客题目链接:https://nanti.jisuanke.com/t/41305 给定的起点是S,终点是T,反向跑一下就可以了,注意判负环以及每次查询需要添加边 AC代码: #include< ...

  9. 2018ACM/ICPC 焦作网络预选赛-A Magic Mirror

    Jessie has a magic mirror. Every morning she will ask the mirror: 'Mirror mirror tell me, who is the ...

随机推荐

  1. 解释一下一门语言该有的东东(Javascript)

    注释 Js中有两种注释 // 单行注释 /**/ 多行注释 变量 变量就像学校学习的 未知数 如 3 + x = 8 x: 类似变量,在改造一下 x + y = z 当 x=3, y=5, z=8, ...

  2. 15分钟让你了解如何实现并发中的Barrier

    说到Barrier,很多语言中已经是标准库中自带的概念,一般情况下,只需要直接使用就行了.而最近一些机缘巧合的机会,我需要在c++中使用这么个玩意儿.但是c++标准库里还没有这个概念,只有boost里 ...

  3. 修改 jupyter notebook的默认文件夹位置

    安装好Anaconda 3以后,就可以使用Jupyter notebook了,但是我们打开Jupyter notebook后,发现界面是一个默认的目录,这个目录在哪里?如果想把自己写的程序文件保存在自 ...

  4. Scala基础语法学习(一)

    1. val和var的区别 val定义的是一个常量,无法改变其内容 scala> val s = 0 s: Int = 0 scala> s = 2 <console>:12: ...

  5. 维恩贝特面试JAVA后台开发

    1 自我介绍 2 链表和数组区别(数组空间连续,且有下标,查找快,但是增删数据效率不高,链表的空间不连续,查找起来慢,但是对数据的增删效率高,链表可以随意扩大,数组不能) 3 sort方法的实现 (A ...

  6. 深入研究BufferedInputStream内幕

    目录 1 概述 2 BufferedInputStream源码分析 3 BufferedInputStream在实际场景中,没有太多用处 4 BufferedInputStream唯一使用场景 1 概 ...

  7. hbase集群region数量和大小的影响

    1.Region数量的影响 通常较少的region数量可使群集运行的更加平稳,官方指出每个RegionServer大约100个regions的时候效果最好,理由如下: 1)Hbase的一个特性MSLA ...

  8. (四十二)c#Winform自定义控件-进度条扩展

    前提 入行已经7,8年了,一直想做一套漂亮点的自定义控件,于是就有了本系列文章. GitHub:https://github.com/kwwwvagaa/NetWinformControl 码云:ht ...

  9. go 学习笔记之是否支持以及如何实现继承

    熟悉面向对象的小伙伴们可能会知道封装,继承和多态是最主要的特性,为什么前辈们会如此看重这三种特性,真的那么重要吗? 什么是封装 什么是封装,封装有什么好处以及怎么实现封装? 相信大多数小伙伴们都有自己 ...

  10. 关于多线程中sleep、join、yield的区别

    好了.说了多线程,那就不得不说说多线程的sleep().join()和yield()三个方法的区别啦 1.sleep()方法 /** * Causes the currently executing ...