2022.11.03
2022.11.03
P4090
没想出来正解,打了一个假的乱搞竟然拿了58分。
因为编号在第一头拿不到礼物的牛之后的牛一定都拿不到礼物,所以我们可以用二分来找出这头牛。
现在重点是如何快速滴check它。说实话挺难想的
二分我们的答案牛 \(mid\),然后将编号 \([1,mid-1]\) 的奶牛按 \(c_i\) 升序排序(让拿完礼物插到队伍较为靠后的奶牛排在前面),我们暂且先不管为什么这样排是对的(后面会讲),但按照贪心的思路这样排可以尽量把我们的 \(mid\) 往前挤,以check它是否能拿到礼物。
容易发现,一头奶牛拿完礼物如果排在了 \(mid\) 后面,就会把 \(mid\) 往前挤一位,但如果拿完礼物插到了 \(mid\) 前面,就会让 \(mid\) 停留在原地。
排完序后,一旦发现一头奶牛(假设它叫i)拿完礼物插到了 \(mid\) 前面,那么根据排序的顺序,i之后的奶牛就都会插到 \(mid\) 前面,让 \(mid\) 无法动弹,这时 \(mid\) 就拿不到礼物了,check函数返回false。
那么为什么可以打乱原本的顺序来check呢?
我们讨论一下:
- \(mid\) 拿不到礼物
本来排完序后可以把 \(mid\) 往前挤的牛在原序中可能会插到 \(mid\) 前面,\(mid\) 更不可能拿到礼物。 - \(mid\) 能拿到礼物
本来排完序后可以把 \(mid\) 往前挤的牛在原序中还是可能会插到 \(mid\) 前面,但是这头插队的牛一定会让其它排到 \(mid\) 后面的牛拿到礼物并排到 \(mid\) 后面,不然就会变成拿不到礼物的情况。
check函数: \(t\)是临时用来存排完序的 \(c_i\) 的数组。
code
bool check(int mid){
for(int i = 1; i < mid; ++i)t[i] = c[i];
sort(t + 1, t + mid);
int now = n - mid;//mid目前的位置
for(int i = 1; i < mid; ++i){
if(t[i] > now)return false;//有牛插到了mid前面
++now;//否则mid向前一位
}
return true;
}