CF 1348F Phoenix and Memory
Posted patt
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CF 1348F Phoenix and Memory相关的知识,希望对你有一定的参考价值。
这个问题可以重新表述为:
给你 $n$ 个区间 $[a_i, b_i]$,$1 le a_i le b_i le n$。
这里,区间 $[l, r]$ 指的是正整数集合 ${l, l+1, dots, r}$。
从第 $i$ 个区间中取一个数 $x_i$,也就是说要求 $a_ile x_i le b_i$。
- 判断是否存在一种取数方案使得所取出的 $n$ 个数两两不同,换言之,使得 $x_1, x_2, dots, x_n$ 恰是 $1$ 到 $n$ 的一个排列?
- 若可能,再判断这样的取数方案是否唯一。若唯一,输出唯一的排列,否则输出任意两个排列。
问题 1。
这是一个经典问题。有两个贪心做法:
贪心做法一
将区间按右端点从小到大排序,按此顺序遍历这些区间,在每个区间中取所能取的最小的那个数。举例言之,有三个区间 $[1, 1], [1, 3], [2, 2]$,排序后是 $[1, 1], [2, 2], [1, 3]$。在 $[1,1]$ 中取 $1$,$[2, 2]$ 中取 $2$,$[1,3]$ 中取 $3$。
贪心做法二
这个做法有 $n$ 个步骤。
$S$ 是一个 multiset,初始为空。
第一步:将左端点等于 $1$ 的区间的右端点加到 $S$ 中。若 $S$ 里的最小值小于 $1$,则无解,否则从 $S$ 里的最小值对应的那个区间中选取 $1$,并把最小值从 $S$ 中删除。
第二步:将左端点等于 $2$ 的区间的右端点加到 $S$ 中。若 $S$ 里的最小值小于 $2$,则无解,否则从 $S$ 里的最小值对应的那个区间中选取 $2$,并把最小值从 $S$ 中删除。
第 $k$ 步:将左端点等于 $k$ 的区间的右端点加到 $S$ 中。若 $S$ 里的最小值小于 $k$,则无解,否则从 $S$ 里的最小值对应的那个区间中选取 $k$,并把最小值从 $S$ 中删除。
此贪心算法的正确性证明:https://codeforces.ml/blog/entry/76555?#comment-613974
容易看出,multiset 可换成小顶堆。
问题 2
顺着上述贪心做法二的思路我们可以判断出取数的方案是否唯一:
在第 $k$ 步中如果 $S$ 中至少有两个元素,并且 $S$ 中的最小值大于 $k$——设 $S$ 中的最小值和次小值对应的区间编号分别为 $i$,$j$——那么我们看能否从区间 $j$ 中取 $k$。如何判断呢?
可以这样做:假设正常情况下从区间 $j$ 取出的数是 $x_j$,我们看一下 $x_j$ 能否从区间 $i$ 中取出。
此解法来自 https://codeforces.ml/blog/entry/76555?#comment-613946
正确性我不会证明。
参考实现:https://codeforces.ml/contest/1348/submission/81636889
扩展
如何求有多少个不同的排列?
以上是关于CF 1348F Phoenix and Memory的主要内容,如果未能解决你的问题,请参考以下文章
[贪心][线段树] Codeforces 1348F Phoenix and Memory
CF1348D Phoenix and Science(思维)
CF1348E Phoenix and Berries(dp)
[CF1348D] Phoenix and Science - 贪心