Python解题 - CSDN周赛第36期

本期有点难度,系统也没有多少bug,算是最近 N 期周赛里质量较高的一期比赛了。


第一题:查找点在自然区间的坐标

定义:实数轴上的一个区间由左右两个端点,假设区间是左闭右开的,例如区间`[0,1)`。

给定一个有序的不重合非负整数区间列表 `range_list` :[ `[0,1)`, `[3,4)` ],该非负整数区间列表将实数轴分割成了这些区间列表 `range_list_nature_ext` :[`(-∞,0)`, `[0,1)`, `[1,3)`, `[3,4)`, '[4,+∞)`]。我们称 `range_list_nature_ext` 为由 `range_list` 扩展的 `自然区间`。

问题:写一个查找算法,对于给定非负整数区间列表 `range_list` ,查找一个非负整数 `p` 落在了 `range_list_nature_ext` 的那个区间,返回那个区间的在 `range_list_nature_ext` 里的下标,我们称这个下标为非负整数 `p` 在 `range_list` 里的 `自然坐标`。

示例:

示例
输入12 2
0 10
15 20
输出2

分析

给出的区间是左闭右开,且互不重合,所以只要用一个列表记录给出的区间左边的数字,就可保证要查询的位置是唯一的。而区间的右边数字用来和下一个区间左边的数字进行比较,如果相同,则说明这两个区间之间没有额外的空间,如果不同(肯定是前者比后者小),则存在一个中间的区间,所以要“插入”区间右边的数字(较小的那个数字)来表示中间区间。

比如示例中给出的 [0, 10) 和 [15, 20),第一个区间右边的数字 10 比第二个区间左边的数字 15 更小,所以存在一个中间区间 [10, 15),于是得到可以用来表示区间的数字为 [0, 10, 15],而且该数字必然是单调递增的。

接下来就比较简单了,遍历该列表,找出第一个比 p 大的数字在列表中的位置,就是答案。如果没有比 p 大的数字,则说明 p 位于最后一个区间,所以其位置就等于列表的长度。

由于列表单调递增,所以查找的时候可以用二分,但对此题区别不大,因为前面遍历区间生成列表已经是 O(n) 的复杂度了,这里使用二分 O(logn),并不会改变整体的复杂度。


第二题:鬼画符门之大师兄恋爱

鬼画符门,每年都会统计自己宗门鬼画符消耗的数量。 往年一直是大师兄管理。 但是大师兄谈恋爱了!!怎么能让这种事耽误自己恋爱时间呢!! 鬼艺接手了!! 你能帮鬼艺写一个程序帮助她统计每年消耗数量最多的鬼画符吗?

示例
输入5
red
red
green
grenn
hen
输出red

分析

本题其实就是找所有字符串里出现次数最多的那个,由于没有说明如果出现次数最多的有多个字符串该输出哪个(同类题目一般是按字典序输出第一个),所以可以认为不会出现多个答案。

使用 Python 内置的 Counter 类将所有出现的字符串转化为字典,然后输出出现次数最多的那个即可。而 Counter 类也有现成的 most_common() 方法。除了输入,计算加输出就两行代码。

参考代码

from collections import Counter
print(Counter(vector).most_common()[0][0])

第三题:去除整数

已知存在集合A包含n个整数,从1到n。 存在m个整数a。 在集合A中去除这m个整数的的倍数。 输出集合中包含的元素的个数。(1<=n<=1e9,1<=m<=10)

示例:

示例一示例二
输入5 2
2 3
27202192 1
725
输出327164672

分析

本题考察一个基本的数学思想——容斥定理。

我们先来看最简单的情况,如果只有一个整数,也就是 m = 1 的情况。比如 n = 20,要去除的其倍数的数字是 2,那么很显然,所有偶数都会被去除,总共需要去掉 2, 4, 6, 8, 10, 12, 14, 16, 18, 20 共十个数。由于结果只需要输出剩下的数字个数,而不需要输出剩下哪些数,所以我们不需要从 1 遍历到 20,而是可以通过简单的计算得到 20 里含有多少个 2 的倍数,也就是 20\div 2,向下取整,得到需要去掉 10 个数,答案就是 20-10=10 。

如果有两个整数呢?比如要去除的是 2 和 3 的倍数。我们如法炮制,先去掉十个2的倍数:2,4,6,8,10,12,14,16,18,20,计算公式为 20\div 2,再去掉六个3的倍数:3,6,9,12,15,18,计算公式为20 \div 3。这时就会发现答案不对了,因为对于那些既是 2 的倍数也是 3 的倍数,比如 6,12,18,我们去除了两次,所以还要把这三个数加回来。而这样的数有多少呢?答案就是 20 \div lcm(2, 3),(lcm 表示两个数的最小公倍数),也就是 20 \div 6 = 3 个。最后的答案就是 20-20 \div 2-20 \div 3+20\div6=7 个。

通过检查三个以上的数的情况,我们可以得到这样一个规律:对于 m 个数,我们要去掉其中奇数个数字最小公倍数的倍数,加上偶数个数字最小公倍数的倍数。比如对于从 n 中去掉三个数字a, b, c 的倍数,我们最后要得到的答案用公式表示即:

ans = n-\frac{n}{a}-\frac{n}{b}-\frac{n}{c}+\frac{n}{lcm(a,b)}+\frac{n}{lcm(a,c)}+\frac{n}{lcm(b,c)}-\frac{n}{lcm(a,b,c)}

由于本题的 m 很小(m\leq 10),所以最多仅存在 2^{10}=1024 种组合。于是我们可以先用内置排列组合函数将 m 个整数的所有长度的所有排列组合找出来,分别计算这些组合的最小公倍数;再用 n 分别与其相除,得到每种组合的倍数的个数;最后再判断每种组合里有几个数,奇数则减去倍数的个数,偶数则加上,最后即可得到答案。

某些题解会使用状态压缩DP来递推组合数的最小公倍数,但由于此题计算规模不大,用不用皆可,因为原理都是一样的,用不用状压只是实现手段不同罢了。


第四题:括号上色

小艺酱又得到了一堆括号。 括号是严格匹配的。 现在给括号进行上色。 上色有三个要求: 1、只有三种上色方案,不上色,上红色,上蓝色。 2、每对括号只有一个上色。 3、相邻的两个括号不能上相同的颜色,但是可以都不上色。 问括号 上色有多少种方案?答案对1000000007取模。

分析

10 期考过的老题,也是CF的原题,难度中等偏上,如果没刷过,能做出来还是很考验经验和编码能力的。而且关键是英文原题的描述并没有被准确翻译成中文:

  • For any pair of matching brackets exactly one of them is colored. In other words, for any bracket the following is true: either it or the matching bracket that corresponds to it is colored.

原题说得很清楚,一对括号里只能有、且必须有一个被上色,但遗憾的是中文描述还是少了这一条,导致部分选手可能产生误解(认为一对括号可以都不染色),浪费了时间。

题目本身质量还是挺高的,可以参考问哥以前写的题解,代码注释的部分也解释得很详细了。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

请叫我问哥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值