Everyone is a winner
题目链接:AGC053D
题目大意
有 n 个人 n 道题目,每个人做其中 ai 个题目需要 1 分钟,bi 个题目需要 2 分钟,ci 个题目需要 3 分钟。
需要你给每个人安排做每道题用的时间,使得第 i 个人是最早做完 i 道题目的人之一。
问你是否能安排出合法的方案。
思路
发现如果我们确定了前
i
i
i 步做什么,剩下的做法是显然的:
就是不要太快,所以是从大到小放。
那前面的也是不能太快,因为你最终到
i
i
i 的时间已经固定,所以我们也是从大到小放。
于是有这么一个猜想过程,我们让 f i , j f_{i,j} fi,j 为 i i i 这个人做了 j j j 题目需要的时间,然后我们有一个限制 T i T_i Ti 就是前 i i i 道题最早被解决掉的时间(在考虑的人之中)。
那先考虑后面的怎么放(从后面往前看是因为最后的位置放的是确定的),那首先是肯定给前面的留空间,所以我们最大化做完 i i i 题需要的时间,接着我们让 3 3 3 尽可能的多(这样不容易比别人快)
那求出上面的
f
f
f 之后,我们可以给
T
i
T_i
Ti 更新限制:
T
y
=
min
(
T
y
,
t
x
,
y
)
(
1
⩽
y
<
x
)
T_y=\min(T_y,t_{x,y})(1\leqslant y<x)
Ty=min(Ty,tx,y)(1⩽y<x)
那我们给前面的留了空间,但是后面的呢?它会不会因此比后面的更快呢?
我们其实可以通过证明发现在只有
1
,
2
,
3
1,2,3
1,2,3 这种耗时的情况下,是不会有的。
考虑分类讨论,如果我们有
T
i
+
2
⩾
T
i
+
1
T_i+2\geqslant T_{i+1}
Ti+2⩾Ti+1(就是这前面
i
+
1
i+1
i+1 个不是都填
3
3
3,因为如果不是,至少会有
2
/
1
2/1
2/1 在最后)
那我们扩展一下对于
j
>
i
,
T
i
+
2
(
j
−
i
)
⩾
T
j
j>i,T_i+2(j-i)\geqslant T_j
j>i,Ti+2(j−i)⩾Tj。
那自然合法。
那如果是都填
3
3
3,那对于前面的一段都填
3
3
3 的,那比它编号小的人要做到前面的某个时刻比他小或者跟它一样,这个时刻又要比它大或者一样,所以就也是全
3
3
3,那后面的数也是一个道理,那这
n
n
n 个人的前
i
+
1
i+1
i+1 个都是
3
3
3,那也就没啥事了。
后面出现了没有
3
3
3,那又是上面的情况了。
所以是对的!
那我们就用一个斜率为
1
/
2
/
3
1/2/3
1/2/3 的凸包来维护
T
T
T 这个东西。
然后这里的
T
T
T 其实我们减可以直接一个一个减,因为这个东西是递减的,均摊下来只会减
O
(
n
)
O(n)
O(n) 次。
然后就是看给你
T
T
T,要你看是否合法:
考虑化一下式子得到一些不等式:
x
+
y
+
z
=
i
x+y+z=i
x+y+z=i
x
+
2
y
+
3
z
=
t
x+2y+3z=t
x+2y+3z=t
(解二元一次方程,以
y
,
z
y,z
y,z 为未知数)
y
+
z
=
i
−
x
,
2
y
+
3
z
=
t
−
x
y+z=i-x,2y+3z=t-x
y+z=i−x,2y+3z=t−x
y
=
−
2
x
−
t
+
3
i
y=-2x-t+3i
y=−2x−t+3i
z
=
x
+
t
−
2
i
z=x+t-2i
z=x+t−2i
x
=
z
−
t
+
2
i
=
3
i
−
t
−
y
2
x=z-t+2i=\dfrac{3i-t-y}{2}
x=z−t+2i=23i−t−y
0
⩽
x
⩽
a
i
,
0
⩽
y
⩽
b
i
,
0
⩽
z
⩽
c
i
0\leqslant x\leqslant a_i,0\leqslant y\leqslant b_i,0\leqslant z\leqslant c_i
0⩽x⩽ai,0⩽y⩽bi,0⩽z⩽ci(这是条件,两个极值分别带入)
2
i
−
t
⩽
x
⩽
c
i
+
2
i
−
t
,
3
i
−
t
−
b
i
+
1
2
⩽
x
⩽
3
i
−
t
2
2i-t\leqslant x\leqslant c_i+2i-t,\dfrac{3i-t-b_i +1}{2}\leqslant x\leqslant \dfrac{3i-t}{2}
2i−t⩽x⩽ci+2i−t,23i−t−bi+1⩽x⩽23i−t
(注意第二个式子的多边界,要在上面
+
1
+1
+1,因为它上面
x
=
x=
x= 的位置不能直接这么看,你真正要满足的是上面方程解出来的结果,所以要记得
+
1
+1
+1,不加样例也过不了)
代码
#include<cstdio>
#include<iostream>
#define INF 0x3f3f3f3f3f3f3f3f
using namespace std;
const int N = 2e5 + 100;
int n, a[N], b[N], c[N];
struct Line {
int a, b, c;//斜率(填1/2/3)对应的截距
Line(int aa = INF, int bb = INF, int cc = INF) {
a = aa; b = bb; c = cc;
}
int get_val(int x) {
return min(min(a + x, b + 2 * x), c + 3 * x);
}
}T;
Line merge(Line x, Line y) {
return Line(min(x.a, y.a), min(x.b, y.b), min(x.c, y.c));
}
void slove() {
scanf("%d", &n); T = Line();
for (int i = 1; i <= n; i++) {
scanf("%d %d %d", &a[i], &b[i], &c[i]);
T = merge(T, Line(b[i] + 2 * c[i], c[i], 0));
}
bool yes = 1;
for (int i = n; i >= 1; i--) {
int t = T.get_val(i), x;
while (t >= 0) {
x = min(a[i], min(c[i] + 2 * i - t, (3 * i - t) / 2));
if (x >= max(0, max(2 * i - t, (3 * i - t - b[i] + 1) / 2))) break;
t--;
}
if (t < 0) {yes = 0; break;}
int y = -2 * x - t + 3 * i, z = x + t - 2 * i;
T = merge(T, Line(y + 2 * z, z, 0));
}
if (yes) printf("Yes\n");
else printf("No\n");
}
int main() {
int T; scanf("%d", &T);
while (T--) slove();
return 0;
}