zoukankan      html  css  js  c++  java
  • CF1354&CF1355 简要题解

    最近打的两场,直接写一块吧。

    CF1355 (Round #643)

    这场比较垃圾,C 和 E 都是一眼会做但是边界要推半天的垃圾题……(当然,其中一个原因是我不太擅长做这种细节题)

    • A

    注意到 (operatorname{mindigit})(0) 的时候,不管怎么迭代,值都不会变。同时,由于每次非 (0) 迭代后,增量在 ([1, 81]) 内,因此迭代 (1000) 次后,百位上必然是 (0)。因此直接模拟,(operatorname{mindigit})(0) 时跳出即可。

    • B

    注意到 (a_i) 较小的人可以放到 (a_i) 较大的人组成的队伍里,但反过来不行;并且 (a_i) 较小的人组成的队伍人更少。因此从小到大考虑每一群 (a_i) 相等的人,尽可能用他们匹配,并将余下的人留给 (a_i+1) 对应的组里去匹配。实现时可以用一个桶来维护每个 (a_i) 对应的人数。

    • C

    (operatorname{solve}(x, y, z)) 表示 (ain [1, x], bin [1, y], cin [1, z])(a, bleq c)(a, b, c) 能组成三角形的方案数。不难发现,原问题等于 (2^3)(operatorname{solve}) 的线性组合(实际上它表示的是一个三维前缀和)。

    对于 (operatorname{solve}(x, y, z)),考虑枚举 (c) 的值,此时 (aleq min(x, c), bleq min(y, c), a+b > c)。如果没有第三个限制,显然合法的 (a, b) 数目是一个矩形,而不满足第三个限制的 ((a, b)) 数目等于沿着 (y=c-x+epsilon) 这条直线裁剪后,左下角的格点个数。而这一部分,在 (c<x+y) 时等于一个大三角形(即所有 (y=c-x+epsilon) 覆盖到的格点)减去 (0/1/2) 个小三角形(即 (a>x)(b>y) 的部分)。注意 (cgeq x+y) 时上面的图形可能会有问题,但是 (cgeq x+y) 时一定不能构成三角形,因此直接跳出即可。

    • D

    可以发现,如果存在一个子集和为 (k),那么只需要反选就可以选到 (S-k);否则,如果能选到 (S-k),反选也可以选到 (k),因此一定选不到 (S-k)。因此只需要考虑构造一个 (k),使得选不出一个子集和为 (k) 即可。

    不难发现,对于 (2nleq S) 的情况,只需要构造 (n-1)(1)(1)(S-n+1),并取 (k=n) 即可。由于 (2nleq S),有 (S-n+1geq n+1>n),因此一定取不出一个子集和为 (n)

    否则是 (2n>S) 的情况,显然至少有一个元素为 (1)。对于任意一种方案,考虑最小的 (i) 个元素,设它们的和为 (t),显然 (tgeq i)。假设 ([1, t]) 的所有元素都能取到,在 (i=1) 的时候显然正确。考虑第 (i+1) 小的元素 (x),它不能超过 (lfloor frac{S-t}{n-i} floor),并且每个 ([x, t+x]) 之间的元素都可以取到(取 (x) 和某个和在 ([0, t]) 之间的子集)。如果存在某个 (kin [t+1, t+x]) 不能取到,则 (t+1<x),此时可以取 (k)([t+1, x-1]) 之间的任意值。由于 (t+1<xleq lfloor frac{S-t}{n-i} floor),可以看出,令 (t) 尽量小时,左右两端的限制都不会变紧,因此 (t=i) 时最优,有 (i+1<xleq lfloor frac{S-i}{n-i} floorleq frac{S-i}{n-i}),若 (x) 存在,则至少需要 (i+1<frac{S-i}{n-i}),由于 (iin [1, n-1]),即 ((i+1)n-i^2<S),移项得 (i(n-i)-n<S-2n)。又 (S-2n<0),可知 (i(n-i)-n<-1),即 (i(n-i)<n-1),显然在 (iin[1, n-1]) 时不成立。因此,不存在这样的 (x),即每个 ([1, t+x]) 之间的元素都可以任意取。

    事实上,一个简单的理解方法是,注意到 “令 (t) 尽量小时,左右两端的限制都不会变紧” 这句话意味着我们应该让最小的 (n-1) 个元素都取 (1),而 (2n>S) 告诉我们,这样显然是不存在合法的 (k) 的。

    • E

    首先发现,如果 (m>a+r),我们大可用一次增一次删来实现移动,因此 (m=min(m', a+r)),其中 (m') 为输入的 (m) 值。

    考虑如果已经知道了最终所有数都等于 (p),如何计算答案。将 (h) 排序后,设 (h_{i}leq p)(h_{i+1}>p),则左边需要增加 (L=sum_{j=1}^i p-h_i) 次,右边需要减少 (R=sum_{j=i+1}^n h_i-p) 次。因此,设 (x=minleft(L, R ight)),费用就是 (mx+a(L-x)+r(R-x))。为了去掉 (min),可以枚举 (L, R) 中较大的一方,并由此可以得到一个不等式解出 (p) 的取值范围。略微推一下式子,可以发现,当 (L, R) 中较大的一方和 (i)(即最靠右的 (h_i) 不超过 (p) 的位置)相同时,费用是关于 (p) 的一次函数。大力分情况讨论即可,具体的推导摸了(

    • F

    首先,设 (x=prod_{p_iin mathrm{prime}} p_i^{e_i}),则 (d(x)=prod (e_i+1))

    一个主观的想法是,由于比较大的质数对答案影响相对来说比较小,我们可以只问出较小的质数的出现次数。并且,由于不超过 (10^9) 的数最多可以表示成 (9) 个不同质因子的积,可以设定阈值 (B),先问出每个不超过 (B) 的质数是否出现,再依次问出它们出现的次数。

    考虑一下 (0.5leq frac{mathrm{out}}{mathrm{ans}}leq 2) 的条件,可以发现,假如只考虑不超过 (B) 的质数时,(d(x)=w),那么输出 (2w) 可以允许 (>B) 的不同质数出现 (0, 1, 2) 个,或是某个质数出现 (2, 3) 次。这意味着,设 (m) 表示已知的所有出现过至少一次的质数的乘积,只要 (mcdot p_icdot p_{i+1}cdot p_{i+2}>10^9),就说明在剩下的质数中任选,贡献也只会在 ([1, 4]) 中,所以直接停止询问,输出 (2w) 即可。

    打一下表,发现在 (mgeq 2) 的时候,每次询问从未询问过的第一个质数出发,在本次询问中尽可能多地将下一个质数乘进去,最多只需要 (20) 次操作就可以满足上面的条件。至于询问已知质数的次数,每次可以询问两个质数最大的不超过 (10^9) 的幂的乘积,打表可以发现,最坏情况下((m=2))也只需要询问 (21) 次。

    唯一的问题是 (m=1) 的时候需要询问不止 (22) 次。但是这个时候,(|mathrm{ans}-mathrm{out}|leq 7) 的条件就可以用上了。如果当前 (m=1),那么只需要保证未知质数的贡献不超过 (8) 即可。即,(p_icdot p_{i+1}cdot p_{i+2}cdot p_{i+3}>10^9) 的时候就可以跳出了,发现这样只需要询问 (4) 次就行了。于是可以通过本题。

    实际上,(m) 是质数的时候,这个 (|mathrm{ans}-mathrm{out}|leq 7) 的条件也可以应用。因为此时 (w) 只有 (2),而未知质数的贡献如果在 ([1, 8]) 内的话,最终答案在 ([2, 16]) 内。只需要输出 (8) 就可以了,我写的最坏情况下需要 (19) 次,标程似乎有一些神秘优化做到了 (17) 次……


    CF1354 (Edu Round #87)

    这一场题目质量比上面那场 div2 不知道高到哪里去了(

    • A

    首先如果 (aleq b),那么第一次就可以醒来,输出 (b)。否则至少需要再睡一次,如果 (cleq d) 的话,在第二次入睡前就会被吵醒,永远也不会睡够。否则需要多睡 (lceil frac{a-b}{c-d} ceil) 次,每次需要 (c) 分钟。

    • B

    枚举右端点,则左端点是最近的,区间内包含了 123 的位置。显然这个位置是其中某个字符最后一次出现的位置,否则向右移动一格后必然还是满足条件的。因此从左往右扫描右端点,维护每个字符最后出现的位置即可。

    • C1

    对于一个正 (4k) 边形,显然它的边可以分成 (k) 组,每组都是两对对边,且同组的两对边的中点连线互相垂直。画一下图就可以看出,任选一组边,将这四条边都卡在正方形的边界上,恰好可以卡到,并且是最优的。它的最优性大概可以理解为,由于正 (4k) 边形恰有 (4k) 条对称轴,我们实际上只需要考虑外接正方形与它的切点在某两条对称轴之间的情况。这时候很容易发现,只有两种合法的切点:在某个角上,或者贴着某一整条边。

    然后是计算长度。由于正 (4k) 边形在这条边上对应的圆心角是 (frac{2pi}{4k}),设重心到这条边中点的距离为 (x),则 (frac{0.5}{x}= an(frac{pi}{4k})),即 (x=frac{1}{2 an(frac{pi}{4k})})。注意 ( an) 的部分只有角度的一半。

    • C2

    对于 (4k+2) 边形,发现一组对边之间的距离是小于它的对角线长度的,因此不能模仿上面。发现正 (4k+2) 边形的对角线,怎么放都是 “最碍事的”,考虑将它放在正方形的对角线上。这里的最优性我确实不会证明,题解也只是模拟了一下旋转过程……(只能说这样算出来刚好能过样例x

    这里的长度略微不太好计算。首先和上面类似地,重心到某个顶点的距离 (w=frac{1}{2sin(frac{pi}{4k+2})})。那么不妨设固定了的对角线上的点分别为 ((-w, 0), (w, 0))。随后需要枚举顶点(由对称性,只需要枚举对角线出发后旋转 (frac{pi}{2}) 内的顶点,即第一象限的即可),对于某个顶点 ((wcos(frac{ipi}{2k+1}), wsin (frac{ipi}{2k+1}))) 来说,如果它成为了恰好卡在正方形边上的点,那么正方形的这条边满足 (x+y=wcos(frac{ipi}{2k+1})+ wsin (frac{ipi}{2k+1})),对所有的 (wcos(frac{ipi}{2k+1})+ wsin (frac{ipi}{2k+1}))(max) 即可。

    注意这样算出来的是正方形对角线的一半长度,乘以 (sqrt{2}) 再输出即可。

    • D

    看起来这个时空之类的限制有什么高论啊,但是我不是很会。

    但是注意到值域只有 (O(n)),因此可以开一个树状数组作为桶,插入时正常插入,删除时需要支持询问第 (k) 大。这里其实只需要将树状数组的大小开到 (2^k),就形成了一个天然的二进制倍增结构,与倍增相同,直接扫一遍就可以了。伪代码如下:

    function findKth (int x)
    	ret := 0
    	for (i : k->0)
    		if a[ret + 2^i] < x
    			ret := ret + 2^i
    			x := x - a[ret]
    		endIf
    	endLoop
    	return ret + 1
    endDef
    

    可以注意到,循环到 i 时,实际上 ret + 2^i 在树状数组上对应的区间长度恰好是 (2^i)。因此这样倍增的时候,每次都选了能选的(即区间内的数字个数小于 (x) 的)最长的区间。最后剩下的 (x) 一定在 ([1, mathrm{times}_{mathrm{ret}+1}]) 之间,因此直接返回 ret+1 是正确的。

    因此这个算法时空分别是 (O(n))(O(nlog n)),而且常数非常小。

    • E

    首先有一个基础的观察:13 的本质是相同的,只有 2 与它们不同。因此这个题的要求实际上是对二分图染色,使得其中一种颜色的点个数恰好是 (n_2)

    但是,注意到题目中并没有保证图联通。因此,我们虽然可以对每个连通块分别算出一个 pair ((x, y)) 表示两种颜色的点数,但是仍然要对每个连通块决策出将 (x, y) 中的哪一个染成 (n_2) 对应的颜色。

    因此,设 (f_{i, j}) 表示前 (i) 个连通块,是否存在一种方案,使得选中的大小恰为 (j)。但是我们还需要输出方案,也就需要记录每个状态的决策。设 (g_{i, j}) 表示考虑前 (i) 个连通块时,大小为 (j) 的状态是否选中了第 (i) 个 pair 的 (x)。这样,由于所有的状态都是 (0/1),就可以用 bitset 优化了(虽然直接 (n^2) 也能过……)。

    • F

    显然最终拥有的单位越多越优,因此只需要考虑恰好保留了 (k) 个的情况。

    对于一个大小为 (k) 的集合,如果已知最终保留这个集合里的单位,考虑什么样的顺序取最优。发现越靠后取的单位,它的 (b_i) 就会产生越多次数的贡献。并且,在取这个集合里 (b_i) 最大的单位之前,可以将所有不在这个集合里的单位都取走,产生 ((k-1) b_x) 的贡献之后删掉(惨 工具人 惨)。因此,设所有单位都按照 (b_i) 排序,最终保留这个集合的答案为 (sum_{i=1}^{k} a_{S_i}+(i-1) b_{S_i}+sum_{t ot in S} (k-1) b_t)。它也可以表示成 (sum_{i=1}^n (k-1) b_t +sum_{i=1}^{k} a_{S_i} - (k-i) b_{S_i}),这样第一个求和的影响就可以看作常数。对于 (S) 内的部分,只需要按照 (b_i) 排序后直接 dp 即可。(所以为什么 (nleq 75) 呢)

    • G

    注意到 (kleq frac{n}{2}),因此随机取一个箱子,取到石头的概率不会小于 (frac{1}{2})。这也就是说,对于任意的一个礼物 (x),随机取一个 (y),返回 (y>x) 的概率不会低于 (frac{1}{2})

    那么,如果想要判断箱子 (x) 是否是石头,只需要随机若干个 (y)。如果询问 ((x, y)) 得到 (y) 较重,那么可以直接判断 (x) 是礼物;否则,如果所有回答都是 (x) 较重或一样重,(x) 是礼物的概率就不会超过 (2^{-k}),其中 (k) 是随机次数。

    因此,我们可以先用 (30) 次询问来判断 (1) 是否是石头。假如是礼物,就可以直接返回;否则,第一个礼物一定在 ([2, n]) 中。设 (operatorname{solve}(l, r)) 表示已知第一个礼物在 ([l, r]) 内的求解过程,可以发现 ([1, l-1]) 的盒子都确定是石头时,这个函数才有意义。这个时候,取 (w=min(l-1, lfloorfrac{r-l+1}{2} floor)),询问 ([l-w, l-1])([l, l+w-1]),由于左边都是石头,可以直接通过返回值来确定 ([l, l+w-1]) 中有没有礼物;按情况分别递归即可。

    至于询问的复杂度,可以发现整个询问流程中,按照 (w) 的增减可以分为两部分。第一部分每次 (w) 都会变成 (2w),最后不超过 (n);第二部分每次 (w) (最坏情况下)都会变成 (lceil frac{w}{2} ceil),可以发现不管怎么样复杂度都是 (O(log n)) 的。由于题目没有在这里卡常,可以很轻松地通过(良心出题人)。

  • 相关阅读:
    提取汉字首字母助手类:
    ComboBox1获取datatable的一列
    C++容器
    字符串反转
    什么是C++标准库
    如何在程序中使用系统调用
    秒针、分针和时针的重合次数(十二小时)
    查找单向链表倒数第n个元素
    Ubuntu下查看计算机信息
    实验三:给系统添加一个新的系统调用
  • 原文地址:https://www.cnblogs.com/suwakow/p/12935070.html
Copyright © 2011-2022 走看看