https://blog.hydd.cf/p/ioi2022/
Day2 T3 Thousands Islands 待填。
Catfish Farm
第 i 列哪些鱼被抓住,只和第 i 列堤的高度,以及 i−1/i+1 列中较高堤的高度有关。
容易想到按列 dp,这样第 i 列就有两种情况,较高的列是 i−1/i+1。
这个较高没有必要,仅仅多加了限制,所以可以每个 i 自由选择 i−1/i+1,这样如果选的不是较高的答案只会更小。
我开始想的是,对于选择 i−1 的,记录第 i 列的高度;对于选择 i+1 的,记录这列抓的鱼最高的高度。这样状态就表示算完了前 i 列的答案,当前是选择 i−1/i+1。
其实不用这样,题目要求我们每一列选择一个堤的高度,就按照题目来,状态改为表示固定完了前 i 列堤的高度,当前是选择 i−1/i+1。选择 i+1 的答案在 i+1 时再计算。
具体来说,记 dp(i,j,0/1) 表示前 i 列,堤恰好不覆盖这一列从下往上第 j 条鱼,当前选择 i+1/i−1(当前列贡献 未计算/已计算)。对于前一列选当前列/这一列选前一列的,要算上增加的贡献。
https://qoj.ac/submission/44615
Prisoner Challenge
比较大小,容易想到在二进制下按位比较。x 需要记录当前存的是第几位,当前位为 ∅/A 的当前位的值是多少。
每次,如果当前记录的是 A,就拿 B 这一位比较,如果相同跳到下一位,否则返回;记录的是 ∅,就把 A 这一位记录进去。
这样次数太多(大概是 3logn),但是用 B 比较完相同后,你其实可以知道 B 下一位的信息,把 B 下一位信息存进去。可以发现,比较是按照位数 B,A,B,A 交替,不需要记录里面存的是 A/B,现在只需要记录位数和当前位的值,大概是 2logn。
一种思路是换进制,k 进制应该是 k⌈logkn⌉,但还是不能通过。
二分和从高到低贪心填是几乎等价的。按照同样思路,可以改为每次把值域范围分成 k 份,看是否在同一个值域区间,次数是 f(n)=f(⌈nk⌉)+k,没区别。
题目保证了两个数不同,故如果当前数为 1 或 n 就直接返回,把剩下的范围再分成 k 份,也就是 f(n)=f(⌈n−2k⌉)+k。
把这个式子拿去 dp,对于不同的 n,k 不固定,f(n)=minkf(⌈n−2k⌉)+k,这样算出来 f(5000) 恰好为 20。就按照 dp 出的方案选 k 即可。
https://qoj.ac/submission/45397
Radio Towers
先考虑固定 D,对于选择的两个信号塔 i,j,中间要存在一个塔 k,Hk≥max(Hi,Hj)+D。
可以发现只有相邻的两个选择的信号塔才需要判断。
对每个 i,找到左右第一个 k,满足 Hk≥Hi+D,分别记作 Li,Ri。
i,j 之间要有满足条件的塔,就要求 Ri≤Lj(i,j 中较大的对应的位置一定满足 k 的条件),也就是区间 [Li,Ri) 两两不交。
同理易证区间只会有包含和不交两种情况(D≥0),对于两个位置 i,j,不妨假设 Hi≥Hj,若 Ri>Lj,则区间 [Lj,Rj) 都不满足条件,即 Ri≥Rj。
不考虑询问的区间限制,D 增加时,L 只可能减小,R 只可能增大,且根据证明,只有 H 大的包含 H 小的,故只会一些不是包含关系的区间变成了包含关系。
按照 D 从小到大,每次把所有包含其它区间的区间全部删去(维护 i 对应区间包含相邻某个的最小时刻),剩下的区间个数就是当前 D 能选的数量。
有询问给定的区间限制 l,r 时,把当前 D 所有剩下的 i 在 [l,r] 内的个数求出来,但这样可能两边各少 0/1 个区间(一个 i 在 [l,r] 内的,包含了一个在 [l,r] 外的被删除了,实际上这个 i 可以选)。
如果求出来的个数不为 0,看其中最小的 L 和最大的 R,看 [l,L),(R,r] 内是否还能选。要求区间内 x<y,Hx−Hy(或 Hy−Hx)的最大值,类似于 ST 表的方法预处理即可。
https://qoj.ac/submission/45344
Digital Circuit
必须有一个叶子节点到根一路都是黑色,但是直接计数会算重。
要每一种方案对应唯一的叶子节点(相当于添加了限制条件)。
树的形态固定,由于根节点为黑色,说明儿子里至少有 p(参数)个是黑色,那么选择其中的第 p 个黑色儿子,往下走。
最终会走到一个叶子节点,则设这种方案对应这个叶子节点。
现在考虑每个叶子节点 x,算对应到它的方案的数量。首先它到根的都必须是黑色,此外其它点的参数毫无关系,因为固定其它点的参数后其它点的颜色固定后,要求对应到的是 x,则 x 到根路径的所有的参数是唯一确定的。
对每个叶子节点预处理出到根路径外的点的参数选择的方案数。线段树支持区间取反操作,维护所有黑色叶子节点的方案数之和即可。
https://qoj.ac/submission/45436
Rarest Insects
把不同的数放在不同的列,这个东西很像一个柱形图(?)
一种思路是每次删掉一行(删掉所有出现过的数各一次,需保证数量始终为 1),次数是行数(最多数出现次数)×n。
另一种思路是每次删掉一列(删掉某种数的所有出现,需保证数量每次必须加 1),次数是列数(数字种数)×n。
两种各做 √n 次后一定能删完(数字总数为 n)。这样是 O(n√n) 次。没什么优化空间。
第一种思路其实可以扩展成每次删掉 r 行,需保证数量始终不超过 r 即可。
求出数字种类数 c。考虑二分答案(上界 nc),用第一种思路来 check 当前答案 mid,是 O(nlogn) 次。
这个可以继续优化,如果 check 成功了(加入了 mid×c 个数),已经加入的数之后就不需要加了;如果 check 失败了,未加入的数之后也不可能加。
这样每次数量接近减半(有个上取整),次数共 2n 左右。再加上外面求种类数的 n,总次数 3n 左右。
这样一交得了 99.89 分,再加点优化,比如已经达到 mid×c 之后的就不需要尝试加入了,再 shuffle 一下序列防止被卡,就通过了。
不知道有没有不用随机化严格在 3n 内的做法?