概述
计蒜客题目链接
题目大意:有 N 个人要洗衣服,每个人到来的时间是 t i t_i ti,有一台洗衣机,洗衣机洗衣服要花费 x x x 单位时间(x 是个变量),且洗衣机一次只能供一个人使用,还可以选择手洗,手洗将花费 y y y 单位时间,求如何安排可以使得最后所有人洗完衣服的时间最短,对每个 x ∈ [ 1 , y ] x in [1,y] x∈[1,y] 输出所有人洗完衣服的最短时间。
设所有人到来的时间从小到大有序,在最优策略下一定存在一个分界点,该分界点之前所有人都是手洗,该分界点(包括这个分界点)之后所有人都使用洗衣机。
感性的证明一下这个结论:
所有人都不使用洗衣机的情况下,答案 ans = tn + y
,答案受限于最后一个人,考虑使用洗衣机,要将答案降下来,必须先给最后手洗的人使用。
考虑从后往前第一个不使用洗衣机的人 p,对于 p 以前的点,再安排洗衣机的使用也无法使得答案降下来,只有考虑让
p
p
p 也使用洗衣机。
当
p
p
p 使用洗衣机后有两种可能的结果:
1、最后的答案减小,这种情况仅发生在此时答案受限于 p 点,且 p 点使用洗衣机后不会抬高最后的时限,这种情况让 p 使用洗衣机更优。
2、最后的答案增大,这种情况发生在答案受限于
p
p
p 后面的点,p 点使用洗衣机抬高了后面的点开始使用洗衣机的时间,这种情况不如让 p 手洗。
综上分析最后一定是一个后缀使用洗衣机。
第 m m m 个使用洗衣机的人,对最后的答案的贡献是 t [ m ] + ( n − m + 1 ) ∗ x t[m] + (n - m + 1) * x t[m]+(n−m+1)∗x,使用洗衣机的人对答案的贡献为 m a x ( t [ i ] + ( n − m + 1 ) ∗ x ) max(t[i] + (n - m + 1) * x) max(t[i]+(n−m+1)∗x)
设界限为点 p,
a
n
s
=
f
(
p
)
=
m
a
x
(
t
[
p
−
1
]
+
y
,
max
i
=
p
n
(
t
[
p
]
+
(
n
−
p
+
1
)
∗
x
)
)
ans = f(p) = max(t[p - 1] + y,displaystylemax_{i = p}^n(t[p] + (n - p + 1)*x))
ans=f(p)=max(t[p−1]+y,i=pmaxn(t[p]+(n−p+1)∗x)),
f
(
i
)
f(i)
f(i) 是一个以 i 为自变量 的凸函数。显然当
x
x
x 增大时,界限 p (极小值点)
将会左移。
考虑倒序枚举 x,界限点单调从 n 向 1 移动,设当前界限点为 i
,若 i - 1
点答案更小,则界限向 i - 1
移动。对于
m
a
x
(
t
[
i
]
+
(
n
−
m
+
1
)
∗
x
)
max(t[i] + (n - m + 1) * x)
max(t[i]+(n−m+1)∗x),这是一个一次函数,可以利用李超树维护,每当界限移动时将直线维护到李超树中,由于每条直线的定义域都相同,维护直线的复杂度为
log
y
log y
logy
官方题解:
整体复杂度为: O ( ( n + y ) log y ) O((n + y) log y) O((n+y)logy)
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 10;
const int N = 1e6;
typedef long long ll;
ll inf = 123456789123456789ll;
int n,m,q,op,s,t[maxn],y;
ll a,b,ans[maxn];
struct Line { //直线结构体
int k,b;
Line() {}
Line(int ki,int bi) {
k = ki, b = bi;
}
ll calc(int x) { //计算在 x 点的 y值
return 1ll * k * x + b;
}
};
struct seg_tree { //维护 x = k 处最低线段
#define lson rt << 1,l,mid
#define rson rt << 1 | 1,mid + 1,r
Line line[maxn << 2];
void build(int rt,int l,int r) {
line[rt].k = 0; line[rt].b = 0;
if (l == r) return;
int mid = l + r >> 1;
build(lson); build(rson);
}
void update(int rt,int l,int r,int L,int R,Line t) {
if (L <= l && r <= R) {
int mid = l + r >> 1;
if (line[rt].calc(l) < t.calc(l) && line[rt].calc(r) < t.calc(r)) {
line[rt] = t;
} else if (line[rt].calc(l) < t.calc(l) || line[rt].calc(r) < t.calc(r)) {
if (line[rt].calc(mid) < t.calc(mid)) {
Line tmp = t; t = line[rt]; line[rt] = tmp;
}
if (t.k > line[rt].k) {
update(rson,L,R,t);
} else {
update(lson,L,R,t);
}
}
} else {
int mid = l + r >> 1;
if (L <= mid) update(lson,L,R,t);
if (mid + 1 <= R) update(rson,L,R,t);
}
}
ll query(int rt,int l,int r,int v) { //查询区间 L,R 最小值
if (l == r) return line[rt].calc(v);
ll ans = line[rt].calc(v);
int mid = l + r >> 1;
if (v <= mid) return max(ans,query(lson,v));
else return max(ans,query(rson,v));
}
}seg;
int main() {
while(scanf("%d%d",&n,&y) != EOF) {
for (int i = 1; i <= n; i++)
scanf("%d",&t[i]);
t[0] = -y;
sort(t + 1,t + n + 1);
int p = n; //最后一个使用洗衣机的人
seg.build(1,1,y);
for (int i = y; i >= 1; i--) {
while (p >= 1) {
ll curval = seg.query(1,1,y,i);
ll curans = max(curval,1ll * t[p] + y);
ll nxtval = max(curval,1ll * (n - p + 1) * i + t[p]);
ll nxtans = max(nxtval,1ll * t[p - 1] + y);
if (nxtans <= curans) seg.update(1,1,y,1,y,Line(n - p + 1,t[p])), p--;
else break;
}
ans[i] = max(seg.query(1,1,y,i),1ll * t[p] + y);
}
for (int i = 1; i <= y; i++)
printf("%lld%s",ans[i],i == y ? "n" : " ");
}
return 0;
}
最后
以上就是幽默萝莉为你收集整理的2019 南京网络赛 I. Washing clothes(思维 + 李超树(模板))的全部内容,希望文章能够帮你解决2019 南京网络赛 I. Washing clothes(思维 + 李超树(模板))所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复