第十三届北航程序设计竞赛预赛题解

以下题解由北航 2017-2018 赛季 ACM-ICPC 集训队金牌、银牌成员共同编写而成,比赛链接为 https://buaacoding.cn/contest-ng/index.html#/188

A. 五角星

出题人和数据 Dshawn

验题人和题解 skywalkert


输入只有 360 360 种,可以模拟每一种情况,将答案打表存到代码里。

实际上只会有两种情况,取决于两个五角星是否完全重合。

B. P5

出题人、数据和题解 coldwater

验题人 chitanda


题目大意

题目的意思是给了一个长度为 n n 0/1 s s ,让你找到最短的不是 s 子串的串长度。其实就是找一个位数 i i ,使得存在一个 x[0,2i) ,且 x x i 位二进制表示不在 s s 中。

出题人一开始想到这道题目的 idea 的时候,想的是用后缀自动机 (SAM) 做。从根节点开始 bfs 后缀自动机,第一个没有 0/1 两个转移的层数,就是答案。后来被 O(n) O ( n ) 做法碾压之后,SAM 的 bfs 做法就沦为对拍程序了。

题目解法

线性复杂度

首先可以证明答案的上界,是满足 2k+k1>|s| 2 k + k − 1 > | s | 的最小的 k k 。我们从这个上界开始递减枚举答案 k。我们用一个 bitset (实际上是一个 int )数组 ok[val] o k [ v a l ] ,来表示 val v a l 这个值的 i i 位二进制表示,是否在串 s 中作为子串出现。

我们从 k k 转移到 k1 的时候,只需要枚举一遍 val[0,2k) v a l ∈ [ 0 , 2 k ) ,然后转移到 val & (2k11) v a l  &  ( 2 k − 1 − 1 ) 即可。 复杂度为 O(n+ki=12k)=O(n) O ( n + ∑ i = 1 k 2 k ) = O ( n )

chitanda 写的代码实际测试 280ms

coldwater 写的 SAM 实际测试 1700+ms

同样复杂度 O(n) O ( n ) 的 SAM 跑得极慢,心情复杂。

loglog log ⁡ log 复杂度

上面说过答案的级别是 O(logn) O ( log ⁡ n ) 级别,我们二分答案,复杂度就是 O(nloglogn) O ( n log ⁡ log ⁡ n )

coldwater 写的代码实际测试 236ms

log log 复杂度

我们不二分答案,也不很聪明地转移 ok ok 数组,复杂度 O(nlogn) O ( n log ⁡ n )

实际测试 683ms

数据构造

答案最大出到了 23 23 ,那么最长的串长就是 222+221=4194325 2 22 + 22 − 1 = 4194325 。构造答案为 k+1 k + 1 的数据的时候,我们要把 [0,2k) [ 0 , 2 k ) k k 位二进制数缩成一个长度为 2k+k1 的串。方法就是构造 2k1 2 k − 1 个点的有向图,边上是 0/1 0 / 1 。从点 x x 分别连到点 (2x+0/1) & (2k11) 的边。然后用 Hierholzer Algorithm O(|E|)=O(n) O ( | E | ) = O ( n ) 的时间复杂度内求一个欧拉回路就可以了。

其他数据半构造加随机的,有的调了调 0 0 1 的比例。

上面说的三个复杂度算法都没有卡,时限给的 1s,感觉出了个垃圾题啊。

C. cgxj 的设计师

出题人、数据和题解 quintessence

验题人 zlc1114skywalkert


首先,根据三角形“头朝下”、“头朝上”可以分为两类,分别对其进行计数。

设上边长度 m m 、下边长度 n (m>n) ( m > n ) 已经给定。

ak,bk a k , b k 分别表示三角形最上方部位处在梯形宽度为 k k 的行上时取得的“头朝下”、“头朝上”的方案数,所求即为 k=nmak+k=nmbk

对于 ak a k ,在该行能取得的正三角形最大边长为 h=kn h = k − n ,该行点数为 k+1 k + 1 ,故 ak a k 的值即为在 k+1 k + 1 个等间距的点中选取两个距离小于等于 h h 的点的方法数。由此易得 ak=(k+1)h(h+1)h2。对其求和可得 mk=nak=(H+23)+n(H+12) ∑ k = n m a k = ( H + 2 3 ) + n ( H + 1 2 ) ,其中 H=mn,(uv)=u!v!(uv)! H = m − n , ( u v ) = u ! v ! ( u − v ) !

对于 bk b k ,因为三角形的最大边长为 min{kn,k2} min { k − n , ⌊ k 2 ⌋ } ,故需要对 k k 的大小进行分类讨论。

计算 bk 时枚举正三角形底边所在梯形位置的宽度,当宽度为 i i 时,正三角形边长为 ki,故这种边长的正三角形个数为 i(ki)+1=2ik+1 i − ( k − i ) + 1 = 2 i − k + 1

  1. k2n k ≤ 2 n ,则 bk=k1i=n(2ik+1) b k = ∑ i = n k − 1 ( 2 i − k + 1 )

  2. k>2n k > 2 n ,则 bk=k1i=k+12(2ik+1) b k = ∑ i = ⌊ k + 1 2 ⌋ k − 1 ( 2 i − k + 1 )

H=mn H = m − n ,将两部分求和可得

  1. m2n m ≤ 2 n ,则 mk=nak+mk=nbk=(H+23)+2n(H+12) ∑ k = n m a k + ∑ k = n m b k = ( H + 2 3 ) + 2 n ( H + 1 2 )

  2. m>2n m > 2 n ,则 mk=nak+mk=nbk=(H+23)+n(H+12)+4(m2+13)+(2(1)m)(m2+12)(n+13) ∑ k = n m a k + ∑ k = n m b k = ( H + 2 3 ) + n ( H + 1 2 ) + 4 ( ⌊ m 2 ⌋ + 1 3 ) + ( 2 − ( − 1 ) m ) ( ⌊ m 2 ⌋ + 1 2 ) − ( n + 1 3 )

由于出现的组合数下标都不超过 3 3 ,用 (uv)=u!v!(uv)! 计算,预处理乘法逆元后可以 O(1) O ( 1 ) 求解。

D. 方奶奶打牌

出题人、数据和题解 ShinriiTin

验题人 zzhskywalkert


显然第一种牌对答案贡献为 a1 a 1

将问题转化一下,变成每一轮选择当前场面存在的一张牌删掉,直到场上没有牌剩下为止。

记事件 Ai,j A i , j 为“第 i i 种牌有 j 张且其中第一张被删的牌比第一种牌第一张被删的牌先删除”, Pr(Ai,j) P r ( A i , j ) 表示该事件在上述过程中发生的概率。

这样对于第 i i (i2) 种牌,对于答案的贡献就是 j=lowiupijPr(Ai,j) ∑ j = l o w i u p i j ⋅ P r ( A i , j )

因此,对于第 i i 种牌,只需要考虑它们与第一种牌被删除的相对先后顺序,而不需要去考虑其它类型牌的干扰。

记事件 Bi,j 为“实数空间内随机生成 (i+j) ( i + j ) 个数,且前 i i 个数中最小的比剩下的 j 个数中最小的数小”, Pr(Ai,j) P r ( A i , j ) 表示该事件发生的概率,则有

Pr(Ai,j)=Pr(Bj,a1)upilowi+1 P r ( A i , j ) = P r ( B j , a 1 ) u p i − l o w i + 1 。

这就类似于 Treap 中合并两个子树时用到的概率模型: Pr(Bi,j)=ii+j P r ( B i , j ) = i i + j

这个概率是如何得来的呢?

它等价为盒子里有 (i+j) ( i + j ) 个两两不同的小球,现在从中随机不放回地取出 i i 个,第一个小球被选到的概率,因此有

Pr(Bi,j)=(i+j1i1)(i+ji)=ii+j

其中 (nm) ( n m ) 表示从 n n 个元素的集合里选出 m 个元素组成集合的方法数,满足 (nm)=n!m!(nm)! ( n m ) = n ! m ! ( n − m ) !

知道了 Pr(Bi,j) P r ( B i , j ) ,就很容易计算出第 i i 种牌对答案的贡献为

1upilowi+1j=lowiupij2j+a1
对于每组数据预处理一下 j2j+a1 j 2 j + a 1 的前缀和即可。

单组数据时空复杂度均为 O(n+max{upi}) O ( n + max { u p i } )

E. 气球派对

出题人、数据、SPJ 和题解 Chielo

验题人 chitanda


题解

容易发现,从圆上或圆内任意一整点开始进行奇偶染色,就将原图转化为二分图了(容易从每个点到出发点的曼哈顿距离的奇偶性证明这一点)。

记转化后的二分图为 B(V=LR,E) B ( V = L ⋃ R , E ) ,那么问题就变为:

选取若干个点构成点集 SV S ⊆ V ,使得每条边 eE e ∈ E 都与 S S 中至少一个点相邻。

实际就是二分图的最小点覆盖问题。

Kőnig‘s 定理,我们就知道了这个 S 的大小恰好等于完美匹配的大小,并且构造方法也在定理中阐述了。(网上搜一下做完了!)

构造与证明

QwQ,我还是好好地给出构造方案和证明吧。

构造

假设已经用匈牙利算法得到了完美匹配的一个方案,从每个 L L 未匹配的点出发,在不清空时间戳的情况下,搜索增广路,即进行 dfs。

正常情况下应该搜不出来新的增广路。

那么 L 中所有的未被访问到、且是匹配点的点集 L L ′ ,和 R R 中所有被访问到的、且是匹配点的点集 R,两者的并集就构造出了最小点覆盖 S0 S 0

用网络流的说法就是:

E E 中的边流量为 1,源点向 L L 的每个点连接流量为 1 的边, R R 的每个点向汇点连接流量为 1 的边。

获得一个最大流的方案后,在残余网络中从源点出发进行染色。

所有源点到 L L 的边跑满并且未被染到的点以及所有 R 到汇点的边跑满并且被染到的点,构成的集合是一个最小点覆盖 S0 S 0 。这个过程和匈牙利的做法本质是相同的。

证明

1. |||| | 最 小 点 覆 盖 | ≥ | 完 美 匹 配 |

由于完美匹配构成的边集都是互不相邻的,即都没有公共的点,而最小点覆盖又需要把所有边都覆盖到,所以 S S 的大小至少要大于或等于完美匹配的大小。

2. |S0|=||

由我们的构造方案知,这等价于证明每条匹配边 e=(u,v) e = ( u , v ) 的两个点染色情况相同。

在残余网络中搜寻的过程中,若 uL u ∈ L 被遍历到,但由于 u u 是匹配点,不是我们遍历过程的起始点,因此它必然是从 v 那里顺着反向边过来的;

而若 vR v ∈ R 被遍历到,那由于这条边是流满的,反向边还有流量,那么 u u 必然也被遍历到。

所以,u 被染色,当且仅当 v v 被染色,逆否命题也成立。

综上,完美匹配中的每条边,两个端点 u v v 的染色情况是相同的。

而我们的构造过程也是从匹配边端点的访问情况进行二选一,所以 S0 的大小恰好等于完美匹配的大小。

3. S0 S 0 是一个点覆盖

假设有条边 e=(u,v)E, e = ( u , v ) ∈ E , uL, u ∈ L , vR v ∈ R ,未被 S0 S 0 中的点覆盖。

引理部分:

3.1.1. 由于 S0 S 0 是从完美匹配中构造出来的,所以匹配边肯定是被覆盖到的,因此 e e 非匹配边

3.1.2. 而若 e 是非匹配边,那么两个点必然不能同时是非匹配点,否则这条边可以用来增广,那么先前的匹配就不是完美匹配了;

3.1.3. 由于 e e 未被覆盖,所以 u v v 均不在构造的 S0 中,这样就可以从 u u v 的染色情况得到 u u v 是否是匹配点的信息。

证明部分:

3.2.1. 假设 u u v 均未被染到,那么 u u 肯定不是遍历的起点,即 u 是匹配点,那么 uS0 u ∈ S 0 ,这个边是覆盖到的,矛盾;

3.2.2. 假设 u u v 均被染到,因为 u u v 均不在 S0 S 0 中,所以 v v 只能是非匹配点,由于不能同时是非匹配点,所以 u 也只能是匹配点,这两个点同时被染到,相当于找到了一条增广路,匹配不是完美匹配,矛盾;

3.2.3. 假设 u u 未被染到,v 被染到,但 u u v 均不在 S0 S 0 中,那这两个点都不能是匹配点,与不能同时是非匹配点矛盾;

3.2.4. 假设 u u 被染到,v 未被染到,但 e e 是非匹配边,在残余网络中是有流量的,u 被染到,接下来肯定会染到 v v ,与构造方式矛盾。

综上,e=(u,v) 不管什么情况,都会有矛盾,所以由反证法可知 e e 不可能存在,因此所有边都被覆盖到了。

证明总结

S0 是一个大小等于完美匹配的点覆盖,而最小点覆盖的大小是大于或等于完美匹配的大小,因此我们找到了 S0 S 0 是点覆盖中,大小取到最小值的那个,所以 S0 S 0 是一个最小点覆盖。

实际,通过上面(乱七八糟)的构造和证明,很容易发现:若 uL u ∈ L 没被染,它肯定是个匹配点;若 vR v ∈ R 被染,如果 v v 是个非匹配点就找到增广路了,所以 v 也肯定是个匹配点。所以构造的时候可以少判一下匹配点的条件。

比赛结果分析

当时,出到这一道的时候图论题还不是很多。所以本来这道题,是按图论 + 定理题 + 中前期出的,运用到了常见的二分图模型——网格图,以及 Kőnig’s 定理

但是听说 OJ 支持 SPJ,所以写了一个 SPJ 试了试,结果没想到好多人卡在“方案”这一步了。

最后通过率 7/9,都是前排选手在后期AC的……看来卡题意了,略遗憾。

这个定理大部分人只用最小点覆盖和完美匹配相等这一点吧,实际定理中是有阐述出如何从匹配构造出点覆盖的,所以学习定理也要学会证明啊。

那么,希望大家能在接下来的决赛中,能够发挥出自己最好的水平!

F. 最小公倍数

出题人、数据和题解 skywalkert

验题人 chitanda


logp(n) log p ⁡ ( n ) 表示最大的非负整数 α α 满足 pαn p α ∣ n ,那么 a a b 的最小公倍数是 c c ,当且仅当对于任意的质数 p max(logp(a),logp(b))=logp(c) max ( log p ⁡ ( a ) , log p ⁡ ( b ) ) = log p ⁡ ( c )

对于本题, nalcm(mb,k) n a ∣ l c m ( m b , k ) 意味着 alogp(n)max(blogp(m),logp(k)) a log p ⁡ ( n ) ≤ max ( b log p ⁡ ( m ) , log p ⁡ ( k ) ) ,而 mclcm(nd,logp(k)) m c ∣ l c m ( n d , log p ⁡ ( k ) ) 意味着 clogp(m)max(dlogp(n),logp(k)) c log p ⁡ ( m ) ≤ max ( d log p ⁡ ( n ) , log p ⁡ ( k ) ) ,其中 p p 是任意的质数。

logp(n)=logp(m)=0,则 logp(k) log p ⁡ ( k ) 可以取 0 0 ,否则 p 一定是 n n m 的质因子,将它们拿出来分类讨论即可确定 logp(k) log p ⁡ ( k ) 的最小值,这个值可能是 0 0 alogp(n) clogp(m) c log p ⁡ ( m )

对于全部的 1i,jP 1 ≤ i , j ≤ P ,分类讨论的次数是 O(P2loglogP) O ( P 2 log ⁡ log ⁡ P ) 的,这是因为 1,2,,P 1 , 2 , ⋯ , P 的质因子个数之和是 O(PloglogP) O ( P log ⁡ log ⁡ P ) 的,这一点可以通过基本不等式从约数个数之和放缩得到,其中 1,2,,P 1 , 2 , ⋯ , P 的约数个数之和是 O(Pi=1Pi)=O(PlogP) O ( ∑ i = 1 P ⌊ P i ⌋ ) = O ( P log ⁡ P ) 的。

为了实现分类讨论,需要对 1,2,,P 1 , 2 , ⋯ , P 计算它们的全部质因子及其幂次,并且能高效地枚举两个数字 (i,j) ( i , j ) 的质因子并集,为此我们可以使用埃拉托斯特尼筛法为每个数字构建一个 (质因子, 幂次) 组成的序列,其中质因子按升序放置,这样可以在合并两个有序表的复杂度下计算质因子并集。

分类讨论后计算 k k 的具体值时,需要计算 pemod(109+7),其中 e9×109 e ≤ 9 × 10 9 ,记 M=9×109 M = 9 × 10 9 。如果在分类讨论时利用快速幂算法计算,则复杂度为 O(P2loglogPlogM) O ( P 2 log ⁡ log ⁡ P log ⁡ M ) ,会超时。注意到对于分类讨论时遇到的某个质数的幂次 pk p k e e 的取值只有 0 ak a k ck c k ,因此只需要预先对于 pk p k 处理可能的值即可,时间复杂度 O(PloglogPlogM+P2loglogP) O ( P log ⁡ log ⁡ P log ⁡ M + P 2 log ⁡ log ⁡ P )

G. 一元二次方程

出题人 constroy

数据和题解 constroyskywalkert

验题人 无


由一元二次方程的判别式 Δ=b24ac Δ = b 2 − 4 a c 可以知道是否有实数解,以及两根是否相等。注意两根不等的情况要按照升序输出,要考虑 a a 的符号。

对于有根的情况,每一个根可以由一元二次方程求根公式 x=b±Δ2a 求出。对于每一个根,可能出现有理数和无理数两种情况,取决于 Δ Δ 是否是整数。

对于有理数解 x=pq x = p q ,可以先处理小数点前的数字位,然后使用竖式除法计算小数点后的信息,由于 q=2a q = 2 a ,所以余数的取值范围是 [0,2a) [ 0 , 2 a ) 。如果余数取零,那么有理数就除尽了,可以得到精确值。如果除不尽,由鸽巢原理可知,循环节的位数至多为 2a1 2 a − 1 ,不循环部分的位数加上循环节的位数也至多为 2a 2 a ,此时要根据循环的部分算出小数点后前 106 10 6 位的信息,再根据小数点后第 (106+1) ( 10 6 + 1 ) 位进行四舍五入,由于循环节内一定存在小于 9 9 的数字位,所以进位也至多往前移动 2a 位。这一部分使用竖式除法可以 O(2a) O ( 2 a ) 统计每种数字位出现次数。输出答案时要注意四舍五入,而不是四舍六入五留双(一些浮点数的舍入方法是这个)。

对于无理数解,看似无法计算其精确分布,但是通过观察样例可以猜想无理数的数字是符合均匀分布的,具体猜想请参考正规数。由于四舍五入的位数足够多,而百分比的要求的精度较小,因此可以大胆输出 10 10 10 10 10 10 10 10 10 10

H. Yk 的美食计划

出题人、数据和题解 Immortals

验题人 skywalkertchitanda


解题思路

题意可简化为每次求出包含一个点的最大子矩阵,由于矩阵以及矩阵中元素较小,故我们可以将矩阵中的 x 元素标为极小的数值(例如 109 − 10 9 ),然后 O(n2m) O ( n 2 m ) 预处理出包含每个点 (i,j) ( i , j ) 的最大子矩阵值 f(i,j) f ( i , j ) O(1) O ( 1 ) 回答询问即可。

预处理

枚举矩阵的上下边界,将包含点 (i,j) ( i , j ) 的问题转化为包含 j j 这一列的问题,之后再用每一列得到的可能的最大值去更新这一列中所涉及到的每一个点。

枚举上下边界可将二维问题转化为一维问题,令当前上下边界内第 k 列的元素之和为 sum(k) s u m ( k ) ,则可以从前往后递推计算以第 k k 列结尾且包含第 k 列的最大子矩阵值 pre(k)=max{pre(k1),0}+sum(k) p r e ( k ) = max { p r e ( k − 1 ) , 0 } + s u m ( k ) ,也可以从后往前递推计算以第 k k 列开头且包含第 k 列的最大子矩阵值 suf(k)=max{suf(k1),0}+sum(k) s u f ( k ) = max { s u f ( k − 1 ) , 0 } + s u m ( k ) ,则对于该上下边界的情况,包含第 k k 列的最大子矩阵值是 pre(k)+suf(k)sum(k)

不妨设对于上下边界为 [L,R] [ L , R ] 的情况时对第 k k 列计算出的最大子矩阵值是 g(L,R,k),则有 f(i,j)=maxLiR{g(L,R,k)} f ( i , j ) = max L ≤ i ≤ R { g ( L , R , k ) } 。不妨定义辅助信息 h(L,R,k)=max{g(L,R,k),h(L,R+1,k)} h ( L , R , k ) = max { g ( L , R , k ) , h ( L , R + 1 , k ) } ,则有 f(i,j)=maxLi{h(L,i,k)} f ( i , j ) = max L ≤ i { h ( L , i , k ) } ,即可 O(n2m) O ( n 2 m ) 预处理出所需信息。

小结

综上所述,我们便在 O(n2m) O ( n 2 m ) 时间内预处理出 f(i,j) f ( i , j ) ,问题也就迎刃而解了。

不幸地是,预赛中无选手通过此题,可能是因为开题时间过晚或者其他原因没能想出 O(n2m+q) O ( n 2 m + q ) 解法,仅有的两次尝试皆为 O(qn2m) O ( q n 2 m ) ,过于暴力。

I. 园艺师

出题人、数据和题解 Cabinfever

验题人 zlc1114


显然,若最后得到的树苗高度越高,则修剪的次数越少。所以我们可以从高到低枚举高度 height h e i g h t ,然后用树型动态规划去检验该高度是否合法。

动态规划的方法是从较深处往较浅处计算当前节点 u u 的深度,其中叶子的深度为 0,非叶节点的深度为孩子节点的深度除 height h e i g h t 外的最大值加一,若在计算过程中出现节点的深度达到 height h e i g h t ,则表示该节点和祖先节点(若存在)之间的边需要被修剪,此外所有根节点的深度均应该为 height h e i g h t ,否则该高度无解。

也可以不做动态规划,而是先对一棵树的所有节点按深度排序,从较深的节点到较浅的节点依次枚举还没被删除的节点,暴力找到这个节点往上走 height h e i g h t 条边的节点,将这个节点的子树删除,如果找不到这个节点则说明该高度无解,这样做一次检验的复杂也是 O(n) O ( n )

时间复杂度为 O(n2) O ( n 2 ) ,空间复杂度 O(n) O ( n )

另外需要注意的是,答案范围是 0 0 到最小深度,若直接从 5000 开始枚举答案,可能会被卡常数。题面提示只有 30% 30 % 的数据满足 n5 n ≤ 5 ,即最多有 30% 30 % 的数据满足最小深度大于等于 1000 1000

大致错误的原因有:细节出现问题;认为高度是对所有树深度求最大公约数,这是错误的;多组数据的初始化没做好。

J. 球交

出题人和题解 braveTester

出题人和数据 skywalkert

验题人 无


设平面 P P 同时过给定球的三个球心,以平面 P xy x y 平面重新建立坐标系,设 F F 为与平面 P P 平行的平面族,即 F={QQP},则任意 PF P ′ ∈ F ,考虑任意一球同 P P P 的交, A=Ball(xi,yi,zi;ri)P A = B a l l ( x i , y i , z i ; r i ) ⋂ P A=Ball(xi,yi,zi;ri)P A ′ = B a l l ( x i , y i , z i ; r i ) ⋂ P ′ ,容易发现在忽略新坐标系中的 z z 坐标后有 AA,因此如果三球有交,则必在 P P 上有交。

于是现在的问题变成了给定三个圆,如何判断三个圆是否有公共部分。

这里科普一个比较通用的 O(nK) 的做法,其中 n n 是圆的个数,K 是预先设定的二分次数。

首先将圆投影到 x x 轴上,然后对这些区间做交,得到区间 I

I I 上二分,设当前值为 mid,将所有圆向 l:y=mid l : y = m i d 上投影,由于 I I 的选取,任意圆同 l 有交。对这些投影做交,如果有交集,则找到,否则,可以知道哪两个圆的投影不相交。这两个圆的交集一定在 l l 的一侧,因此单独做交,如果没交则无交集,否则,根据交中任意一点位置,定出左或右端点变成 mid 即可。

设定一个 ϵ ϵ ,二分若干次之后,如果 I I 的长度小于 ϵ 还没有被判定为有交,则认为不存在交。

根据精度需要设定二分次数即可。

顺带一提,如果你是分类讨论爱好者,你可以根据三圆相交的 13 种情况确定三圆是否有交,但是依旧需要注意交集为一个点的若干情况。

K. 士郎与凛的日常训练

出题人、数据和题解 chitanda

验题人 coldwater


题目描述了一个双方决策公平的组合游戏,利用 Sprague–Grundy 定理可知,该游戏状态可以使用一个等价的非负整数 NIM 值 表示。在双方均使用理想策略的前提下,若 NIM 值 0 0 ,则先手必败,否则先手必胜。在下面的解法中,我们采用 SG(n) 表示题目所述组合游戏状态的 NIM 值,并且不加证明地使用定理,一个公平游戏状态的 NIM 值 等于它所有后继状态的 NIM 值 组成的集合中没有出现的最小非负整数,形式化的表述为 SG(n)=mexm is a successor state of n{SG(m)} S G ( n ) = m e x m  is a successor state of  n ⁡ { S G ( m ) } ,其中 mex m e x 表示集合中没有出现得最小非负整数。

打表可以发现, n n 2k 2k+11 2 k + 1 − 1 之间的 SG S G 值具有长为 k+2 k + 2 的循环节,且循环节的 k+2 k + 2 个值为 0 0 k+1,而到 2k+1 2 k + 1 处会产生新的 SG S G 值。

其实很容易理解,当 n n 位于 [2k,2k+11] 之间时,能消耗的魔力值的取值范围为 [1,k+1] [ 1 , k + 1 ] ,根据 mex m e x 操作很容易推出其循环节的性质。而当 n n 2k+1 时,能消耗的魔力值得取值范围变为 [1,k+2] [ 1 , k + 2 ] ,那么其后续状态的 SG S G 值分别为 0 0 k+1 的整数,故 SG(2k+1)=k+2 S G ( 2 k + 1 ) = k + 2 。根据这个规律,我们可以把所有 2k 2 k 2k+11 2 k + 1 − 1 之间的循环节预处理出来。

对于一个询问的 n n ,找到最大的 k 满足 2kn 2 k ≤ n ,根据 (n2k)mod(k+2) ( n − 2 k ) mod ( k + 2 ) 的值,就能快速算出 SG(n) S G ( n ) ,使用 Java 自带的高精度或者手写 C/C++ 高精度都是可以通过的。

实际上,对于这题而言,我们只需要知道 SG(n) S G ( n ) 是否为 0 0 ,因此只需要关注每个循环节中 0 的位置即可。

L. 浪哥的烦恼 2

出题人、数据和题解 skywalkert

验题人 无


注意到 1 1 n 的任意一条路径必然经过了每条边奇数次,因此可以确定路径长度一定为 n1i=1di+n1i=12kidi ∑ i = 1 n − 1 d i + ∑ i = 1 n − 1 2 k i d i ,其中 ki k i 是非负整数。

问题转化为求解 2d1,2d2,,2dn1 2 d 1 , 2 d 2 , ⋯ , 2 d n − 1 的线性组合能表示出多少个不大于 mn1i=1di m − ∑ i = 1 n − 1 d i 的非负整数。若 n=2 n = 2 ,则可以直接求出答案,否则注意到它们的线性组合一定是它们的最大公约数的倍数,从而使得数字范围至少能除以 2 2 。以下只考虑 n>2 且做过最大公约数化简之后的处理。

考虑直接使用完全背包的方法进行计算,即有 n1 n − 1 种物品要去凑出不超过 M M 的某些价值,则可以将物品去重,从小到大枚举不能被之前的物品表示的物品,再枚举已经能表示的价值,尝试表示出还不能表示的价值,这样的复杂度是 O(nM) 的,但是注意到每个价值维护的信息可以用一个 bit 表示,使用压位并行的技巧可以做到 O(nMw+wM) O ( n M w + w M ) ,其中 w w 表示一次运算可以计算的 bits 数量,例如 64 位计算机上可以使得 w=64

也可以注意到这 n1 n − 1 种物品的最小值一定是不超过 Mn1 M n − 1 的,设这个最小值是 k k 。将所有能被表示的价值按照模 k 的余值分成 k k 类,不难发现,如果 v 能被表示,那么 v+k v + k 也能被表示,如果可以求出这 k k 类中每一类能被表示的最小值,那么可以很轻松地统计不超过 M 且可被表示的全部值。已知模 k k 0 的最小可表示值是 0 0 ,使用 n1 种可能的物品可以构造出不同模值之间的转移,利用 dijkstra 算法求解单源最短路径,时间复杂度是 O(Mn1(n1)logMn1)=O(MlogM) O ( M n − 1 ( n − 1 ) log ⁡ M n − 1 ) = O ( M log ⁡ M ) ,也可以通过。存在 O(M) O ( M ) 算法。

实际上由于 “ n1i=1dim ∑ i = 1 n − 1 d i ≤ m 时才有解”的限制,在数据范围不迷惑人的情况下,这道题很难生成较强的数据,比赛中通过的做法也基本上是第一种做法去掉压位并行实现的暴力算法。

考虑到题目考察点在于建模,没有构造太多卡暴力的数据,或许有个验题人可以使得这道题的数据更加严格。

M. 最优卡组

出题人、数据和题解 skywalkert

验题人 无


题目给出 k k 个集合,定义一种方案是从每个集合选出一个数字进行求和,问求和结果最大的 n 种方案对应的结果。

下面给出两种做法,一种是优化了后继状态数量的传统做法(使用),一种利用了二分答案的性质。

首先介绍二分答案的做法,主体思想是检查是否存在至少 n n 个结果不小于猜测的答案 v,从而确定第 n n 大的结果是 V,再生成大于 V V 的全部结果,使用 V 补全至 n n 个结果即可。

检查的过程是从最大的解调整得到其他大于 v 的解,得到 n n 个立即停止,并且保证每次在 O(1) 的时间内生成一个新的合法解,新的合法解不大于生成它的解,且不与之前的任意一个解相同。

实际上就是为 ki=1ci ∏ i = 1 k c i 种可能的选择确定一个扩展顺序,使得除了最大解之外的每个选法只有一个前继选法,并且前继选法的结果是大于等于它的。这里仅给出一种可行的扩展方案,其充分性和完备性留给读者自己证明。

对于每个集合 Si S i ,将其元素降序排序后变为序列 Si,1,Si,2,,Si,ci S i , 1 , S i , 2 , ⋯ , S i , c i

对于不同的序列 S1,S2,,Sk S 1 , S 2 , ⋯ , S k ,按照其 Si,1Si,2 S i , 1 − S i , 2 的值排升序,设排序后序列依然为 S1,S2,,Sk S 1 , S 2 , ⋯ , S k

为每个解定义状态为 (val,x,y) ( v a l , x , y ) ,表示当前解的结果为 val v a l ,当前解选择了 Sx S x 中第 y y 大的元素,在 Sx+1,Sx+2,,Sk 中均选择了最大的元素,并且之后扩展出的解也不会修改 S1,S2,,Sx1 S 1 , S 2 , ⋯ , S x − 1 之中的选择。扩展方法如下:

  1. y<cx y < c x ,则当前解可以扩展到 (valSx,y+Sx,y+1,x,y) ( v a l − S x , y + S x , y + 1 , x , y )
  2. x+i<k x + i < k ,则当前解可以扩展到 (valSx+i,1+Sx+i,2,x+i,2) ( v a l − S x + i , 1 + S x + i , 2 , x + i , 2 )

检查过程中,初始解为 (ki=1Si,1,1,1) ( ∑ i = 1 k S i , 1 , 1 , 1 ) ,每次可以根据 y y 进行扩展,也可以升序枚举 i 进行扩展。进行第二类扩展时,如果遇到新解不合法的情况,则不需要继续枚举更大的 i i 进行扩展,这一点是由于序列排序的性质决定的。这样即可 O(n) 构造出不超过 n n 组可能的合法解,总时间复杂度为 O(nlogV)

第二种做法是解决第 n n 大问题的一种传统做法,使用堆从某些初始状态不断扩展出可能的前 n 大状态。不难发现二分答案做法中的扩展其实满足条件,只是后继状态过多,若能将后继状态的数量降低至常数,则可以在 O(knlog(kn)) O ( k n log ⁡ ( k n ) ) 的时间复杂度内解决此题,其中 k k 表示最多的后继状态数量。

为了优化二分答案中的第二类扩展方法,我们重新定义状态 (val,x,y,z),其中 val,x,y v a l , x , y 的定义与原来相同,而 z z 0/1,表示当前状态是否由某个状态从第二类扩展得到,则扩展方法如下:

  1. y<cx y < c x ,则当前解可以扩展到 (valSx,y+Sx,y+1,x,y,0) ( v a l − S x , y + S x , y + 1 , x , y , 0 )
  2. x+1<k x + 1 < k ,则当前解可以扩展到 (valSx+1,1+Sx+1,2,x+1,2,1) ( v a l − S x + 1 , 1 + S x + 1 , 2 , x + 1 , 2 , 1 )
  3. x+1<k x + 1 < k z=1 z = 1 ,则当前解可以扩展到 (val+Sx,1Sx,2Sx+1,1+Sx+1,2,x+1,2,1) ( v a l + S x , 1 − S x , 2 − S x + 1 , 1 + S x + 1 , 2 , x + 1 , 2 , 1 )

充分性和完备性依旧留给读者自己证明。

Written with StackEdit.

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值