第二届东南大学大学生程序设计竞赛(秋季) - 题解

admin 2020-11-30 21:54:02 2020-12-01 15:11:48

第二届东南大学大学生程序设计竞赛(秋季)命题组

A. 姬哥的约会计划

fi,gi,hif_i,g_i,h_i 分别表示第 ii 天与一号约会、二号约会或者不约会时,前 ii 天最多能约会多少次,那么:

fi=max(fi1,hi1)f_i = max(f_{i-1},h_{i-1})

gi=max(gi1,hi1) g_i = max(g_{i-1},h_{i-1})

hi=max(fi1,gi1,hi1)h_i = max(f_{i-1},g_{i-1},h_{i-1})

B. 姬哥的数学课

将序列 an,bna_n,b_n 分别分解到质因数上,然后若 AA 的每一个质因数的幂次的数列都大于等于 BB 的就说明 AABB 的倍数。

分解质因数是只需要分解到根号即可,因为每一个数至多含有一个大于根号的质因数。

C. Emmm: An Easy Programming Warm-up

由于本质不同的询问只有 nn 种,预处理出前缀和后 O(n2)O(n^2) 预处理出参数为 ii 的询问的答案,即可对每次询问 O(1)O(1) 处理,复杂度 O(n2+m)O(n^2+m)

D. Mental Out

线段树维护区间乘积。

由于f(x)x2,f(1)=1f(x)\leq \dfrac{x}{2},f(1)=1,每个数被修改的次数不大于log2ai\log_2{a_i},因此修改时不需要处理全为11的区间,对于大于11的数暴力修改即可。

若使用根号级别复杂度的算法求f(x)f(x)可能导致超时。

E. 梅园理发店

本题可以看成有两条流水线:剪头线和理头线。显然答案不小于两条线单独取最小值的较大值,也即max(m+2,2n)max(\sum m + 2, 2*n)

下面给出一种可以取到该值的构造方案:

按m从小到大排序,所有人先在洗头线第一次洗头,剪头线按洗头线的顺序剪头,容易证明,剪头线会一直工作直到所有人都剪完。对于所有人的第二次洗头,排在第一次洗头人的后面,能洗就洗,第二次洗头的顺序和第一次洗头的顺序相同。构造完毕。

当然本题比较简单,只需输出最小花费时间,也即max(m+2,2n)max(\sum m + 2, 2*n),时间复杂度O(n)O(n)

F. 阿畅和神符

显然,这道题需要分别计数的是每个结点的赏金神符会被取走的道路的条数。而对于任一道路和其上的某一结点 ii ,该结点的赏金神符被取走的条件是该条道路上没有神符价值比 PiP_i 更大。

解法一:按照神符价值从大到小考虑每个结点。当一个结点被考虑过后,将该结点从树上删除,由此会将一棵树变成一座森林。对于正在计数的某个结点 ii ,只需要考虑森林中其所在的这一棵树,因为只有完全在这棵树内部且经过 ii 的道路会取走 ii 上的神符。那么,只要我们以 ii 作为树根,计算得到以 ii 的所有孩子为根的子树的大小,就可以得到道路的总数。为了计算每棵子树的大小,我们考虑该树在原来的树中的形状,则现在的以孩子为根的子树分为在原来树中也是 以ii 的孩子为根的子树和本来是以 ii 为根的子树的外部区域的一棵子树(则该子树的根是原来树中 ii 的父亲)。对于任一结点 jj ,我们维护原来树中以 jj 为根的子树中所有结点在现在的森林中仍然和 jj 连通在一棵树中的结点个数 SiS_i ,或者维护被剥离出去成为新树的总的结点个数(两者之和就是以 jj 为根的子树的大小),那么对于在原来树中就是 以ii 的孩子为根的子树,现在的子树大小就是 SsoniS_{son_i} ,另一方面,对于在外部的子树,我们可以求得现在的 ii 所在树在原来树中对应的根 kk (即 kkii 现在所在树中在原来的树里深度最小的那个结点),则 SkSiS_k-S_i 即为所求。

要维护SS 数组,我们只需在删除结点 ii 时,对于 ii 的所有祖先结点 fafa , 从 SfaS_{fa} 中减去 SiS_i 就可以,直到某个祖先结点已经被删除为止(也就是做到结点 kk)。这可以通过树链剖分实现。另一方面,求取 kk 的操作可以通过树的dfs序加线段树实现,维护每个结点的所有已被删除祖先中最近的那一个。

总时间复杂度:O(nlog2n)O(nlog^2 n)

解法二:按照神符价值从小到大考虑。对于正在被考虑的结点 ii ,有且只有经过它且只经过已考虑结点的道路会选择其上的神符。所以我们立刻想到通过并查集维护结点的连接关系。我们按照价值从小到大插入结点。当一个结点 ii 被插入时,我们枚举它所有相邻结点是否已被插入,是的话就将其所在连通块纳入考虑范围,按照前述方法可以计算出道路条数。之后,将所有连通块与 ii 连接成为新连通块。注意合并操作时同时维护连通块的大小。

总时间复杂度:O(nα(n))O(nα(n))

G. Hmmm: A Tricky Counting Challenge

以下数列 {ai}\{a_i\} 指的均是原数列的任意一种排列。设

b1=a1,bn=an,bi=max(min(maxj=1i1aj,maxj=i+1naj),ai)(2in)b_1=a_1,b_n=a_n,b_i = \max(\min(\max_{j=1}^{i-1} a_j, \max_{j=i+1}^n a_j), a_i) (2 \le i \le n)

{ai}\{a_i\} 得到 {bi}\{b_i\} 的过程称为一次变换。对于任意一个数列 aia_i,对 (ai,bi)(a_i,b_i) 进行排序使 bib_i 单调不升并且 bib_i 相同时 aia_i 单调不降,结果记做 (ai,bi)(a'_i,b'_i),则 bib'_i 仍是 aia'_i 经过变换后的序列。利用动态规划统计所有可能的 {bi}\{b'_i\} 和有多少种取值即可。即用 aia_i 中的元素去填 bib_i,满足填出的 bib_i 单调不降,并且第 ii 大的元素至少要在第 ii 个位置之后才可以出现。设 f[t][i][j]f[t][i][j] 表示考虑了 {ai}\{a_i\} 中前 tt 大的元素,每个元素可以不使用或使用一次或多次,填了 b[1..i]b[1..i],这 ii 个数的和是 jj 时是否有可行的方案,转移类似完全背包,用 std::bitset 优化即可。

H. 姬哥带土豪

不难看出按照题设条件,姬哥用到的背包数量一定不会超过nn,因此不妨设有nn个背包,第ii个背包的剩余容量记为xix_i。初始时xi=C(1in)x_i=C(1\leq i\leq n),可以用线段树来维护xix_i的区间最大值。在线段树递归更新时,若当前节点的左子节点最大值大于等于当前钻石体积,则代表左半区间中必有背包的容量足够放下当前的钻石,此时向左递归;否则向右递归。最终答案即为Σi=1n(xi<C)\Sigma_{i=1}^n(x_i<C),算法复杂度为O(nlogn)O(nlogn)

I. 姬哥与屋子

以最左边的点为中心对剩下 2n12n-1 个点进行极角排序,选取最左边的点和排序后第 nn 个点即可。

J. 丢人的题目

本题实际上是求模 pp 意义下的二次剩余最多有多少个。(若 x2y(mod p)x^2\equiv y(mod\ p),称 xx 为模 pp 意义下 yy 的二次剩余)

对于 pp 是一个奇素数的情形,若非零值 xxyy 的二次剩余,那么 pxp-x 也是。而 1p11\sim p-1 中较小一半的数字都是不同数字的二次剩余(否则,存在 x,yx,y 使得 x2y2(x+y)(xy)0(mod p)x^2-y^2\equiv(x+y)(x-y)\equiv 0(mod\ p),且 xy≢0(mod p)x-y\not\equiv 0(mod\ p),可见 x+y0(mod p)x+y\equiv 0(mod\ p);但注意到 0<x+y<2p2=p0<x+y<2\frac p2=p,所以 x+y≢0(mod p)x+y\not\equiv 0(mod\ p),矛盾)。由此可见,这时 00 有一个二次剩余,近半数字有 22 个二次剩余,而余下数字没有二次剩余。

而对于一个无平方因子的奇合数,求其每个数字的二次剩余前可分别求模其每个素因子意义下的所有二次剩余(二次剩余的数目集合为 {0,1,2}\{0,1,2\}),将模其每个素因子意义下的二次剩余均挑出一个来,再用中国剩余定理组合即可得到模这个奇合数意义下的一个二次剩余;而且由中国剩余定理可知每次挑选有一项不同则最后二次剩余必然不同。所以答案最多是 2n2^n(nn 为素因子个数)。注意到 11 的二次剩余恰有 2n2^n 个,所以它即为答案。

综上,本题答案即 2n(mod 998244353)2^n (mod\ 998244353),用快速幂求解即可。(本题中复杂的 pp 构造描述仅为防止通过最直接的打表得到正确结论)

K. 背包问题·改

首先我们将物品按照自己的归属排个序并且把必选物品排在这一类的第一个。

fi,j,gi,jf_{i,j},g_{i,j} 分别表示考虑了前 ii 个物品,已经选择的物品体积是 jj 时第 ii 个物品所在类的必选物品已经放入了/没有放入 时的最大价值,然后状态定义转移即可,转移方程略。

L. 函数重载

本题可先用树的结构存储 DeclareType 的定义的情况;接下来采用暴力预处理出所有情况的答案或者预处理欧拉环游序的方法实现对 A 类型是否是 B 类型的基类的常数时间判断;随后按多关键字具体度对所有 DeclareFunc 进行排序;对于每个询问直接输出具体度最高的定义即可,最终时间复杂度是平方级别的。

M. 姬哥与卖菜

可以发现,按 ai+bia_i+b_i 为关键字对所有同学排序,即为最优解。排序后逐个统计破防值即可。

证明如下:

考虑队伍中 aM+bMa_M+b_M 最大的同学 MM,下证其必然排于队尾。

假设其不排于队尾,则有同学 NN 排于同学 MM 后,且 aM+bMaN+bNa_M+b_M\geq a_N+b_N。考虑交换同学 NN 与同学 MM ,则其他同学的破防值显然不会改变。而同学 MM 的破防值由 Σi=1M1aibM\Sigma_{i=1}^{M-1}a_i - b_M 变为 aN+Σi=1M1aibMa_N+\Sigma_{i=1}^{M-1}a_i - b_M,同学 NN 的破防值由 aM+Σi=1M1aibNa_M + \Sigma_{i=1}^{M-1}a_i -b_N 变为 Σi=1M1aibN\Sigma_{i=1}^{M-1}a_i - b_N

aM+bMaN+bNa_M+b_M\geq a_N+b_N 可得不等关系:

aN+Σi=1M1aibMaM+Σi=1M1aibNΣi=1M1aibMaN+Σi=1M1aibMΣi=1M1aibNaM+Σi=1M1aibN\begin{align} a_N+\Sigma_{i=1}^{M-1}a_i - b_M &\leq a_M + \Sigma_{i=1}^{M-1}a_i -b_N\\ \Sigma_{i=1}^{M-1}a_i - b_M &\leq a_N+\Sigma_{i=1}^{M-1}a_i - b_M\\ \Sigma_{i=1}^{M-1}a_i - b_N &\leq a_M + \Sigma_{i=1}^{M-1}a_i -b_N \end{align}

故交换操作后,破防值的最大值减小或不变。可见无论其他同学如何排列,经过有限次交换操作后,最优解中同学 MM 必然在队尾。而队尾的同学 MM 实际上对其他同学的破防值无影响。由此通过归纳法可知,最优解为 ai+bia_i+b_i 为关键字的升序排列。