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),不管它了),要算这个:

[sum_{i<j}lcs(S[1dots i],S[1dots j])(pre_j-pre_i) ]

我们建出这个串的 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

[codechef July Challenge 2017] Chef and Sign Sequences

c_cpp CodeChef-事件

c_cpp CodeChef-TruthAndDare

c_cpp CodeChef-AppyLovesOnes

c_cpp CodeChef-ChefAndRidges