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

Everyone is a winner

题目链接:AGC053D

题目大意

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

思路

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

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

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

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

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

那如果是都填 \(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=i-x,2y+3z=t-x\)
\(y=-2x-t+3i\)
\(z=x+t-2i\)

\(x=z-t+2i=\dfrac{3i-t-y}{2}\)
\(0\leqslant x\leqslant a_i,0\leqslant y\leqslant b_i,0\leqslant z\leqslant c_i\)(这是条件,两个极值分别带入)
\(2i-t\leqslant x\leqslant c_i+2i-t,\dfrac{3i-t-b_i +1}{2}\leqslant x\leqslant \dfrac{3i-t}{2}\)
(注意第二个式子的多边界,要在上面 \(+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;
}
posted @ 2022-10-08 19:27  あおいSakura  阅读(38)  评论(0)    收藏  举报