CodeChef 2020 July Long Challenge 题解
Posted 1000suns
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CodeChef 2020 July Long Challenge 题解相关的知识,希望对你有一定的参考价值。
第一次打 CodeChef Long Challenge。
居然阿克了?(当然除了 challenge 题)
然而中途几题还是想了好久,我好菜啊……
最后几天被叫去 whk 了,最后 challenge 连 AC 都没拿到。(虽然拿到了也还是 rk2)
还是 Div.2,菜死了。
CHEFSTR1
模拟。
CRDGAME
模拟。
ADAKING
前 (k) 个格子空着,其它格子有障碍,然后左上角放王就行了。
PTMSSNG
算是经典题的二维版本吧。
对于每个 (x),都应该出现了偶数次。若出现了奇数次,那么缺失的点的 (x) 坐标就是它。
可以用别的方法,但是把所有 (x) 异或起来的结果就是答案的 (x) 坐标。
(y) 同理。
时间复杂度 (O(n))。
CHFNSWPS
首先,每个数在 (A,B) 里总共出现的次数都应该是偶数。否则无解。
然后我们就知道 (A) 要把哪些数给 (B),(B) 要把哪些数给 (A)。
我们要把 (A_i) 给 (B),(B_j) 给 (A),有两种情况:
- 直接交换,代价 (min(A_i,B_j))。
- 找到这两个序列中最小的数 (x),不妨设 (x) 在 (A) 中,那么 (x) 和 (B_j) 交换,(x) 和 (A_i) 交换,发现 (x) 还在 (A) 中。代价 (2x)。
有没有可能有别的情况,比如选多个 (A) 给 (B) 和 (B) 给 (A),然后有一起操作的好方法?
没有。简单证一下:
首先若一个 (A_i) 到了 (B),肯定不会回到 (A)。所以可以把 (A_i,B_j) 直接交换的扔出去(实际上是上面第一种情况)。
设剩下 (2k) 个数,(A) 要给 (B) 其中 (k) 个,(B) 要给 (A) 其中 (k) 个。
剩下的每次操作都只可能还原一个数(用不是这 (2k) 个数的和它交换),所以至少要 (2k) 次操作。而一次交换最小代价是 (x),总代价最小就是 (2kx)。那么进行 (k) 次上面的第二种情况是等价的。
那么问题就是配对 (A_i) 和 (B_j),使得 (min(A_i,B_j,2x)) 的和最小。
若我们将 (A) 要给 (B) 的数从小到大排序为 (C),(B) 要给 (A) 的数从小到大排序为 (D),个数都为 (k),那么一定是 (C_i,D_{k-i+1}) 配对。感性理解就是对于一个很小的 (C_i),一定要把一个很大的 (D_j) 拖下水,防止有更大的花费。
时间复杂度 (O(nlog n)) 或更优秀。
DRCHEF
感觉好神仙啊,为什么那么多人会做啊 /kk
可能又是杀鸡用牛刀了,欢迎来吊打这个做法 /kel
(其实这个做法要想到不难,但是正确性可能有点难证)
免责声明:以下的“杀光了”只是一种便捷表述,并不与题面里任何病毒有关事物的挂钩。
显然,若对一个 (>2x) 的数 (a) 操作,那么对整个局面唯一的影响是 (x) 变为 (2x)。
现在考虑所有 (a) 相等怎么做:
若 (xge a),那么可以一个一个进行操作,每次操作都能杀光一个,答案为 (n)。
若 (x<frac{a}{2}),那么肯定需要花一次操作将 (x) 变为 (2x)。
若 (frac{a}{2}le x<a),那么操作一次不能杀光任何一个,但是之后就能随便杀。
综上,答案为 (n+max(0,lceillog_2frac{a}{x} ceil))。
注意到上面有一步十分优秀:那就是一个一个进行操作,全部杀光。
考虑所有 (le x) 的 (a_i),从大到小一个一个杀,就能做到这种效果。
不妨先将 (a_i) 从小到大排序。
现在我们有一个较优解(注意不一定是最优):不停对 (a_n) 进行操作,直到 (xgefrac{a_n}{2})。此时我们对 (a_n) 再进行一次操作:
- 若此时 (a_n) 被杀光了,那么 (x) 一定大于所有的数,再花 (n-1) 次操作即可。
- 若此时 (a_n) 没有被杀光,变成了 (a_n‘),且 (a_n‘gefrac{a_{n-1}}{2}),那么对 (a_n‘) 再来一次操作后(此时 (n) 被杀光了),(x) 仍然不小于 (a_{n-1}),就可以花 (n-1) 步杀光剩下的。所以总共要再花 (n) 步。
- 若此时 (a_n) 没有被杀光,变成了 (a_n‘),且 (a_n‘<frac{a_{n-1}}{2}),那么对 (a_{n-1}) 来一次操作(此时 (n-1) 被杀光了),(a_n‘) 翻倍后仍然不大于 (x),也可以花 (n-1) 步杀光剩下的。所以总共要再花 (n) 步。
我们得到了 (n+max(0,lceillog_2frac{a_n}{x} ceil)) 步的操作。(其实就是操作直到 (xge a_n),最后再用 (n) 步)
注意到对于 (i e n),如果不是为了一下杀光它,就根本不会动它。因为无论之前多动多少轮,都需要 (xge a_i) 才能最终把它杀光,而之前动 (n) 不会让 (x) 变小。
上面这个做法哪里不够优秀呢?因为最后我们要花 (n-1) 步杀光剩下的数,但实际上可以在把 (x) 变大的中途顺便杀掉一些。
我们肯定是选择某些时刻,找到最大的 (le x) 的 (a_i)(如果有),然后把它杀光。如果 (a_i) 足够大,就可以让 (x) 变小的值不足以影响答案。
但是有没有可能找到最大的几个 (le x) 的 (a_i) 杀光呢?
不可能。多杀一个会使得 (xgefrac{a_n}{2}) 的步数变多至少 (1),而至多让后面杀光全部的代价变少 (1)。
所以我们设 (f_i) 表示我们上一个顺带杀光了 (a_i)(所以此时 (x=2a_i))的最小代价。提前把多杀的个数减掉,后面集体杀光的时候就不管了。
转移,(f_i=min(f_j+max(0,lceillog_2frac{a_i}{2a_j} ceil),max(0,lceillog_2frac{a_i}{x} ceil)))。注意不需要考虑往前再多杀几个了。
统计答案时,枚举最后一个顺带杀光的是哪个(如果有,没有就直接从 (x) 开始),简单算。(其实如果把式子写出来会发现就是 (f_n+n),虽然 (f_n) 本身的意义不明确)
注意到 (log_2) 的取值只有 (log) 个,同时注意到 (f_i) 单调不减,分段转移,每一段取左端点转移即可。
时间复杂度 (O(nlog a_i))。
DRGNDEN
感觉比前两题简单多了。
如果从 (i) 到 (j) 的路上有 (k) 阻挡(注意到一定有 (h_k>h_j)):
- 如果 (h_k<h_i),由于权值都是正的,不如从 (i) 到 (k) 再到 (j)。
- 如果 (h_kge h_i),那就是走不过去。
所以注意到如果往右走到 (j),那么一定有 (pre_j=i),其中 (pre_i) 表示 (i) 前面比 (h_i) 大的最后一个。
往左走同理。
注意到 (pre) 形成了森林,如果加一个超级根,那么问题就转化为给定一棵树,支持操作:
- 单点修改;
- 给出 (u,v),若 (u) 不是 (v) 的祖先则无解,否则求链上点权和。
树剖可以做到 (O(nlog n+qlog^2n)),树上差分可以做到 (O((n+q)log n))。
LCMCONST
显然每个质因子互相独立,拆开看,问题就变成了限制 (max(b_u,b_v)=w)。
先求出每个 (b_i) 的上界 (upr_i)。
如果存在一个 ((u,v,w)) 满足 (w>upr_u,w>upr_v),则无解。
否则,如果存在一个 (i) 满足 (upr_i=+infty),则无数个解。
否则一定有限且不为零个解(让每个 (b_i=upr_i) 即可)。
先考虑 (O(n2^n)) 怎么做(别容斥,容斥就没救了):
枚举 (b_i) 是不是 (upr_i),通过前面的信息就可以推出来当前的 (b_i) 是否一定得是 (upr_i)。
数据范围提示很明显折半。
枚举后半部分的状态,就能知道前半部分哪些得顶到上界,设为 (S),让 (a_S) 加上这个后半部分的方案数。
再枚举前半部分的状态,看有多少个后半部分的方案能满足它。设前半部分状态是 (T),那么后半部分的方案数是 (T) 的所有子集的 (a) 的和。
对 (a) 进行高维前缀和即可。
时间复杂度 (O(n2^{n/2}pcnt))。(pcnt) 是不同的质因子个数。
EXPREP
感觉再次杀鸡用牛刀了 /fad
对于左右端点是 (i,j(i<j)) 的串,会贡献 ((pre_j-pre_{i-1})(lcs(S[1dots i-1],S[1dots j])+1))。(lcs(S,T)) 表示串 (S,T) 的最长公共后缀长度。
(lcs+1) 拆开,(1) 的部分随便做,(lcs) 的部分,先特判 (i=1)(其实就是 (0),不管它了),要算这个:
我们建出这个串的 SAM。说好打死都不用 SAM 的呢?真香!
在 parent 树上,求出每个子树里有哪些前缀。合并多个儿子时,就看别的儿子有多少个比它小的加上。
用线段树合并可以做到 (O(nlog n+n|Sigma|))。
DYNHUL
场上思路:
怎么求给出的参数 (m) 的对应方案啊,不会,自闭了。
搞 whk 时思路:
我发现我是个 sb。
每次先建出凸包,如果要删的点不在凸包上肯定不优,只看凸包上。
枚举要删哪个点,看它旁边两个点,对这之间的点(当然除了目前枚举的点)求凸壳。这个凸壳就会替换掉目前这个点相邻的两条边。
时间复杂度显然是 (O(n^2))。
然后求一个方案的代价,倒过来弄个动态凸包面积,(O(nlog n))。
瞎退退应该能退个不错的成绩,不太清楚。
以上是关于CodeChef 2020 July Long Challenge 题解的主要内容,如果未能解决你的问题,请参考以下文章
[codechef July Challenge 2017] IPC Trainers