【AGC053D】Everyone is a winner(贪心)(凸包)

Everyone is a winner

题目链接:AGC053D

题目大意

有 n 个人 n 道题目,每个人做其中 ai 个题目需要 1 分钟,bi 个题目需要 2 分钟,ci 个题目需要 3 分钟。
需要你给每个人安排做每道题用的时间,使得第 i 个人是最早做完 i 道题目的人之一。
问你是否能安排出合法的方案。

思路

发现如果我们确定了前 i 步做什么,剩下的做法是显然的:
就是不要太快,所以是从大到小放。
那前面的也是不能太快,因为你最终到 i 的时间已经固定,所以我们也是从大到小放。

于是有这么一个猜想过程,我们让 fi,ji 这个人做了 j 题目需要的时间,然后我们有一个限制 Ti 就是前 i 道题最早被解决掉的时间(在考虑的人之中)。

那先考虑后面的怎么放(从后面往前看是因为最后的位置放的是确定的),那首先是肯定给前面的留空间,所以我们最大化做完 i 题需要的时间,接着我们让 3 尽可能的多(这样不容易比别人快)

那求出上面的 f 之后,我们可以给 Ti 更新限制:Ty=min(Ty,tx,y)(1y<x)
那我们给前面的留了空间,但是后面的呢?它会不会因此比后面的更快呢?

我们其实可以通过证明发现在只有 1,2,3 这种耗时的情况下,是不会有的。
考虑分类讨论,如果我们有 Ti+2Ti+1(就是这前面 i+1 个不是都填 3,因为如果不是,至少会有 2/1 在最后)
那我们扩展一下对于 j>i,Ti+2(ji)Tj
那自然合法。

那如果是都填 3,那对于前面的一段都填 3 的,那比它编号小的人要做到前面的某个时刻比他小或者跟它一样,这个时刻又要比它大或者一样,所以就也是全 3,那后面的数也是一个道理,那这 n 个人的前 i+1 个都是 3,那也就没啥事了。
后面出现了没有 3,那又是上面的情况了。

所以是对的!

那我们就用一个斜率为 1/2/3 的凸包来维护 T 这个东西。
然后这里的 T 其实我们减可以直接一个一个减,因为这个东西是递减的,均摊下来只会减 O(n) 次。

然后就是看给你 T,要你看是否合法:
考虑化一下式子得到一些不等式:
x+y+z=i
x+2y+3z=t
(解二元一次方程,以 y,z 为未知数)
y+z=ix,2y+3z=tx
y=2xt+3i
z=x+t2i

x=zt+2i=3ity2
0xai,0ybi,0zci(这是条件,两个极值分别带入)
2itxci+2it,3itbi+12x3it2
(注意第二个式子的多边界,要在上面 +1,因为它上面 x= 的位置不能直接这么看,你真正要满足的是上面方程解出来的结果,所以要记得 +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; }

__EOF__

本文作者あおいSakura
本文链接https://www.cnblogs.com/Sakura-TJH/p/AGC053D.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   あおいSakura  阅读(26)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示