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;
}
posted @ 2022-11-04 11:12  Cotsheep  阅读(18)  评论(0编辑  收藏  举报