test200503

test 200503

T1 互质数组

得分情况

期望:100

实际:100

题意

​ 给你一个包含\(n\)个元素的数组\(a_1, a_2, \dots, a_n\),现在你可以在任意位置插入任意多个整数,问最少要插入多少个数才能使得整个数组的相邻元素互质?两个数 \(p\)\(q\) 互质当且仅当它们的最大公约数是 \(1\)\(b_1, b_2, \dots, b_t\)最大公约数指的是这些数的公共约数中最大的那一个。

正解

​ 就这?

​ 没啥好讲的吧直接每两个相邻数判下 \(\gcd\) 就ok了吧。

​ 全机房的人都切了……

T2 线段

得分情况

期望:100

实际:10

改后:100

题意

​ 现在给你 \(n\) 个线段,每个线段的左端点是 \(l_i\) ,右端点是 \(r_i\) ,现在问每个线段包含了多少个线段(不含自身)

一个线段 \([a, b]\) 包含了线段 \([c, d]\) 当且仅当 \(a \le c \le d \le b\)

对于 \(100\%\) 的数据,\(1 \le n \le 10^5, 1 \le l_i \le r_i \le 10^9\)

犯傻原因

​ 不知道为什么明明可以树状数组搞一下结果我写了平衡树还出锅了……

正解

​ 将所有线段按照左端点从大到小排序,那么对于排在后面线段的左端点一定是在排在前面的左端点的左边的,因此此时考虑包含关系时我们只需要考虑右端点的情况,即对于当前线段,统计出排在当前线段前面的那些线段中,有多少个线段的右端点小于当前线段的右端点即可,这个可以用树状数组很轻松地完成,但是注意左端点相同的线段应该要一起处理。

T1 选取元素

得分情况

期望:50

实际:60

改后:100

题意

​ 给出一个包含\(n\)个元素的数组\(a_1, a_2, \dots a_n\),现在要你在这个数组中选取尽可能多的元素,使得这些元素的最小公倍数不超过\(m\)\(b_1, b_2, \dots, b_t\)的最小公倍数被定义为这些数公共倍数中最小的那一个。

​ 对于\(50\%\)的数据,\(1 \le n \le 100, 1 \le m \le 1000, 1 \le a_i \le 100\)

​ 对于\(80\%\)的数据,\(1 \le n \le 10^6, 1 \le m \le 5\times 10^4, 1 \le a_i \le 10^9\)

​ 对于 \(100\%\) 的数据,\(1 \le n, m \le 10^6, 1 \le a_i \le 10^9\)

犯傻原因

​ 一开始直接暴力枚举了最小公倍数,然后我也不知道然后了……大概就这么水了个60分

正解

​ 受到50分做法的启发,我们不难想到可以直接枚举最小公倍数是多少然后进行计算,假设我们枚举的最小公倍数为\(x\),那么我们可以选择的元素个数为:

\[f(x) = \sum_{i = 1}^n [a_i|x] \]

​ 而我们所求的答案即为:

\[\min_{1 \le x \le m} f(x) \]

​ 但是直接按照上面的式子计算的复杂度是\(O(nm)\)的,不能承受,但是观察发现,\([a_i|x]\)\(1\)当且仅当\(a_i\)\(x\)的倍数,因此如果我们统计出\(a_i\)\(cnt[a_i]\)个,那么我们就可以用如下式子快速计算\(f(x)\)

\[f(x) = \sum_{d|x} cnt[d] \]

​ 因此通过枚举约数的方法,我们可以将总复杂度降到\(O(m^{1.5})\),可以拿到80分。

​ 注意到80分做法当中,\(f(x)\)的计算可以进一步优化,我们考虑\(cnt[d]\)会对哪些\(f\)产生贡献,观察发现,\(cnt[d]\)只会对\(f\)\(d\)的倍数的那些位置产生贡献,因此复杂度将为\(\displaystyle O\left(\sum_{d = 1}^{m} \dfrac{m}{d}\right) = O(m \log m)\),可以通过所有数据。

Tx

得分情况

期望:0

实际:20

改后:

题意

​ 给出一棵\(n\)个节点的无根树,每个节点有一个权值 \(w_i\) ,你可以从选定任意节点为根开始以任意顺序深度优先遍历这棵树,问在你得到的遍历序列 \(a_1, a_2, \dots, a_n\) 中,前 \(k\) 个节点 \(a_1, a_2, \dots ,a_k\)最小权值最大是多少。

犯傻原因

大概真的就是想不出要怎么写了……然后直接sort一下输出了第 \(k\) 大……

结果莫名奇妙水到了20分?

正解

​ 不难观察到答案是具有二分性质的,因此可以考虑二分答案,即二分最小权值,那么问题就转化成了给出一棵树,树上某些节点是可遍历的(权值大于等于二分出来的答案)某些节点是不可遍历的(权值小于二分出来的答案),问从任意节点开始最多能够遍历多少个节点。

​ 这个问题可以通过树型DP解决,考虑当前枚举了某个节点\(r\)为根,设\(dp[x]\)表示在以 \(x\) 为根的子树中最多遍历的节点数目,转移的话可以考虑是否可以访问完某棵子树,如果能就把这棵子树加入到答案中,如果不能那么找一个\(dp\)值最大的即可。

​ 于是我们可以枚举根节点,然后取其中的最大值,判断是否大于\(k\)即可,时间复杂度为\(O(n^2\log n)\),可以拿到80分。

​ 我们发现实际上80分的做法做了很多重复计算,实际上我们完全可以将枚举根的操作换成考虑根的移动,具体来说,我们以\(1\)号节点为一开始的根,考虑根的移动,假设根从\(a\)移动到了\(a\)的一个儿子\(b\),那么此时\(b\)是根,它的\(dp\)值的变动部分只不过是\(b\)的父亲外面的那一棵“子树”,因而我们只需要判断是否能够完全遍历外面那棵子树即可,这个可以通过维护\(a\)周围节点的\(dp\)值的最大值和次大值得到,因而我们复杂度可以降到\(O(n \log n)\)

考试总结

这次考试分数比较低的主要原因还是在第二题写挂了,然后真的直接废了……

以后有时间可以对拍一下看看自己有没有出现什么错误,然后再交,保证正确性。

posted @ 2020-05-03 20:02  ztz_cpp  阅读(145)  评论(0编辑  收藏  举报