北航第十二届程序设计竞赛网络预赛题解

本次比赛共有 401 个用户通过至少一道题目,平均通过 3 题,第一位做出全部题目的同学用时 45:26:37

如果有对题目的疑问或是见解,欢迎寄刀片给出题人。

预祝大家在决赛中取得好成绩!没有搜索引擎了看你怎么办(¬︿̫̿¬☆)


A. 一尺之棰

出题人是 constroy
这是一道贪心题,答案是 (m+1)2n1
也可以按每一天模拟,每次乘 2 后加 1,模拟 n 天也会得到这个结果。
注意答案会超出 int 的表示范围,可能需要使用 long long 等类型表示答案。

B. 加法运算

出题人是 Dshawn
这个题目分两步进行运算:

  1. 计算 1+2++n 的进位次数。
  2. 在我们知道上面这步怎么做了以后,二分答案是计算到哪个数字 R 结束。

对于第一步,我们可以从低位到高位(个位,十位,百位,……)依次来计算每一位的和,例如 1+2++11 的情况,个位就加了 1+2++9+0+1=46 , 十位加了 1+1=2 。 那么个位数往十位就进位了 4610=4 次。接下来就用大数加法的原理从低位到高位把每一位进位次数累计即可。唯一要注意的就是当 9167 + 992 这种情况,最高位还会进位一下,不要落下了。
对于第二步,我们可以用 1+2++R 的进位次数减去 1+2++(L1) 的进位次数算出来 L+(L+1)++R 的进位次数,从而得知最大的 R
由于二分查找的次数是 O(log(107)) ,每次计算进位的复杂度是 O(10) ,所以复杂度为 O(10log(107))
还有一种方法是直接计算每一位的进位次数,从低到高第 i 位的进位次数可以考虑所有数模 10i 后直接加起来的数值超过 10i 多少次, 复杂度和上面相同。

C. 圆圆的物理实验

出题人是 dale
枚举每个实验,如果两成绩均大于等于 3 则加上该积分。

D. 多米诺骨牌

出题人是 constroy
根据期望的线性性可知,答案是每个骨牌被检查前没倒下的概率之和(因为每个骨牌只会被检查一次,题目可能存在歧义)。
每个骨牌倒下的概率只跟它上面和左面的骨牌有关,这题就做完了。
注意不要用 float 类型表示概率,这会使得答案的精度不够。
有的选手使用整数除以 100 表示概率,整数除以 10000 表示答案,注意不要用 int 类型表示答案对应的整数,最坏情况下答案是 nm ,乘以 10000 后已经超出 int 的表示范围了。

E. 区域赛练习题

出题人是 IImare
首先读懂题意后,容易知道答案便是

12i=1nj=1n[ij](Ai+Bi+Aj+BjAi+Aj)

但是 n 太大,直接计算必然超时。
换一种角度思考 (Ai+Bi+Aj+BjAi+Aj) ,它代表着从 (Ai,Bi) 走到 (Aj,Bj) 且只能向上或者向右走的走法数量,那么本题就可以转化为求从任意的 (Ai,Bi) 走到任意的 (Aj,Bj) 一共有多少种走法,这道题就转化为简单的 f(i,j)=f(i1,j)+f(i,j1) 动态规划问题了,时间复杂度 O(40002)
注意 (Ai,Bi) 走到 (Ai,Bi) 的方案是不该统计的,而 (Ai,Bi) 走到 (Aj,Bj)(Aj,Bj) 走到 (Ai,Bi) 的意义相同,可能造成重复计算。
由于数据比较随机,所以 O(4000n) 的方法也可能通过此题。

F. 点与多边形

出题人是 Tangjz
这是一道脑经急转弯类型的题目,可以证明 m 的取值只有 4 ,或者 m 不存在,即二维平面内由整点组成的正 m 边形只可能是正方形。这里给出一个简单的证明。
首先可以发现,只要存在由有理数点组成的正 m 边形,那么可以将所有坐标乘以通分后的分母,从而得到一个整点组成的正 m 边形,因此要证明由整点组成的正 m 边形不存在 (m4) 则要有由有理数点组成的正 m 边形不存在。
假设由有理数点组成的正 m 边形存在 (m4) 。考虑一个由有理数点组成的正 m 边形,它的质心也是有理数点,质心到顶点的距离平方也是有理数,设这个数为 r2。考虑使用 叉积 计算这个正 m 边形的面积,可以发现它的面积也是有理数,而它的面积公式为 12mr2sin(2πm) ,由有理数之比是有理数可知, sin(2πm) 也是有理数。
然而满足 m3,m4sin(2πm) 是有理数的解是不存在的,相信这个问题大家已经在数学分析中解决,这里不再赘述。
对于本题,可以得到的解法是枚举正方形一条边上的两个顶点,检查其他两个顶点是否存在。而查询点集中是否存在某个顶点可以利用哈希算法做到每次操作均摊 O(1) ,因此总的时间复杂度是 O(n2)=O(max(n)n)
注意到有些参赛选手使用 C++ STL 中的 map 进行点集的维护,但由于查询使用了 [] 操作符导致超时,这是由于 [] 操作符会把不在集合中的元素默认添加到集合中导致的,只需要改用 map\count() 即可。
由于数据的构造方法较弱,进行一些剪枝也可以 O(n4) 通过。

G. 火柴棍摆数字

出题人是 Dshawn
题目简单易懂有 trick 但是 trick 已经被标明了。
由于 1 只用 2 根火柴棍,7 只用 3 根火柴棍,所以很简单就能想到只用 17 就能完成 N5 所有数字的摆法。
方法是分奇偶讨论:N 为奇数时,最大数字为 71111 (共 n321),次大数字为 17111 (共 n321);N 为偶数时,最大数字为 11111111 (共 n21),次大数字为 77111111 (共 n621)。
唯一的 trick 就是 N=4 的时候次大的不能按照正常的构造,只有 114 满足条件。
然而此题不想卡人,题目中说如果不存在的话要输出 I am so stupid,有输出这句话的人请左转面壁。
本题也可以把每个数字的数值视为价值,需要的火柴棍视为体积,利用动态规划求解最优价值和次优价值。

H. 黑白相簿

出题人是 Tangjz
如果你看出题目要求的就是 所有单色矩形的面积之和 ,那么也许你已经发现这是一道 悬线法 的基础练习题(悬线法确实不仅限于解决最大子矩形问题)。
对于 01 可以分别处理,所以只用考虑矩阵中一种颜色的情况。
预处理出每个点向上方可以延伸到的同种颜色的长度,枚举一行作为单色矩形的下边界,再枚举一列作为单色矩形的右边界,尝试计算答案。
考虑到随着左边界的递减,可行的上边界只会更低,所以利用栈结构维护可能的每段上边界(显然是单调的区段),以及上边界对应的左边界的取值范围(对应的左边界是连续的一段)。
每个右边界的单色矩形可能不是在当前位置就被统计的,而是在右边界发生变化时,上边界的取值范围可能会发生变化,在这个时候再考虑哪些之前的右边界可以适用这个上边界(满足条件的右边界也是连续的一段),将这个上边界对应的左右边界整理出来,计算它们对应的单色矩形的面积之和即可。
因为在每一行时,列(右边界)变化时只会新增一个上边界,而栈中的上边界只会离开栈一次,所以上述算法的时间复杂度是 O(nm)
由于悬线法实际上是枚举了下边界具体值,枚举了上边界的取值范围,找到对应的左右边界的取值范围,所以稍作修改也可计算出 每种尺寸的单色矩形的个数 ,然后再计算答案,有兴趣可以试一试。

I. NAIVE 排序

出题人是 ez_fwtt08
题意很简单,就是给 M 个字符串,求出其中第 N 小的字符串是什么。但是显然 sort 是会超时的,毕竟数据有 30MB ,时限只给 1s 。(原本内存限制是 64MB ,还可以卡掉正规字典树的做法)由于某些不可描述的原因,出现了ASCII为 123 的字符,请注意。
当时想的是必须使用类似字典树的思想,但是不建树,因为只需要找其中一条线,其他线直接就可以放弃,因此没必要建树。但是后来发现这个数据量(数据是随机生成的)卡不住 nth_element ,所以使用这个 C++ STL 函数也能通过。(或许构造一下数据能卡掉这种做法,但是由于时间比较紧,就放开了)
因此正解是类字典树的划分(即整体二分)或者 nth_element注意:为了提高读写速度和减少内存使用,必须使用 char 数组,不要使用 string
nth_element 比较简单,就是直接调用函数 std::nth_element 就可以了,但是这样做比较慢,而且应该是刚好数据比较随机才能 AC 。这个 nth_element 的复杂度说是均摊线性,但是最坏情况还是 O(MlogM),可能存在一种数据能使它变得很慢。
还有就是被认定为正解的类字典树划分(整体二分)。首先对整个数组的第一个字符进行统计,因为ASCII 码只有 33122 ,所以开一个小于 100 的数组就可以了。然后通过基数排序的思想,我们可以得知第 N 小的字符串的首字符是什么。这也可以看作字典树的第一层。然后可以看第二个字符,但是这时候要把第一个字符已经不符合要求的筛去,出题人的做法是扫一遍,把第一个字符不是答案字符的字符串指针直接交换到数组最后,然后数组大小减一。之后每一个字符都用类似的方法处理,第 i 个字符就相当于字典树的第 i 层的一个分支,其他不在这条线路上的串都已经被筛掉了,筛掉的串里面如果有 k 个是小于 N 的,那么 N 就减掉 k ,然后继续求第 N 小。最后结束的条件是要么串剩下的所有串都到了结尾,或者没到结尾但是只剩 1 个字符串,前者随便选一个字符串即可(因为它们都是相等的),后者可以直接结束。
时间复杂度 O(|Sinput|)30M 大概也就是 3107 左右。标程跑了 424ms ,看到有些同学能跑到 300ms 左右,很厉害啊。

J. 勾肥大战

出题人是 CabinFever
因为本题中假定了屠夫不会影响友方单位的肉钩,所以我们可以对每个己方屠夫单独考虑。
考虑己方屠夫 a 和敌方屠夫 b ,如果 a 能勾到 b ,那么被勾的地方 Ca,b 初始位置 A,B 会形成一个三角形 ΔABC 。设出钩到被勾到的时间为 tBAC=α|AB|=l , 则 |AC|=vat,|BC|vbt
因为我们求的是范围,希望 α 尽量大,由 cosα=AB2+AC2BC22ABBC 可知, |BC|=vbt 时可以达到最大。代入余弦公式后,我们可以得到 cosα=v2av2b2lvat+l2va1t ,这样我们就得到了一个 cosα 为因变量, t 为自变量的函数。显而易见,这是一个 对勾函数 ,当 t=lv2av2b 时, α 取得最大值,此时代入 |AC|,|BC| 就会发现 BCAB
但是,因为屠夫是不能越过峡谷的,所以可能取不到最大值,这时候就需要算出他们在 y 轴相遇的点,这个可以解一元二次方程求得。
γj 为第 i 位己方屠夫到第 j 位敌方屠夫的射线与 y 轴正方向的夹角,对第 i 位己方屠夫可得 θ1=min1jm(γjαj1),θ2=max1jm(γj+αj2)
采用一些技巧也可以避免解方程时产生精度损失,但是本题不卡精度。

K. 空间旅行

出题人是 adginqrw
将距离起点步数相同的点视为同一层,则答案为点数最多的层中的点的个数。
由于总点数个数小于等于 2106 ,所以可以枚举每个点,算出每一层有多少点,进而得到答案。

L. 裁纸片

出题人是 shell0011
直接的想法是枚举纸片的一个点,然后枚举纸片的大小,再判断该纸片是否合法。由于枚举的复杂度较高,判断合法性需要用预处理来降低复杂度。以枚举纸片左上角为例,我们预处理出 R[i][j]D[i][j] 表示 (i,j) 这个点向右和向下最多可以连续多少个完整的小纸片。这样就能在常数级复杂度时间内判断合法性了。时间复杂度 O(n2m)

M. 最小内积

出题人是 yic
首先考虑问题的第一部分:如何使内积最小?以二维向量为例, (x1,x2)(y1,y2)=x1y1+x2y2 。由于各分量互不相同,不妨设x1<x2,则

(x1,x2)(y1,y2)(x1,x2)(y2,y1)=x1(y1y2)+x2(y2y1)=(x1x2)(y1y2)

因此 (x1,x2)(y1,y2)<(x1,x2)(y2,y1) 当且仅当 y1>y2
推广到多维的情况,若存在 i,j(ij) 使 xi<xjyi>yj ,则我们可以交换 yiyj 以获得更优的结果,因此最终结果一定有 ij[1,n](xixj)(yiyj)<0 ,即 A⃗  的最小分量对应 B⃗  的最大分量, A⃗  的次小分量对应 B⃗  的次大分量,……,以此类推。
知道了最终的对应关系后我们来考虑如何交换,稍加思考可知,对于一对位置 (i,j) ,交换 A⃗  与交换 B⃗  的结果是一样的,因此可以只交换 A⃗  的分量,问题转换为给一个数列,要排列成指定顺序的最少交换次数。
我们从任意一个数出发,找到它的目标位置及这个位置上的数字,再找到新数的目标位置,这样不停找下去,由于不可能有两个不同的数对应同一个位置,因此最终必然形成一个长度为 L 的环,不同的环之间没有必要进行交换操作,在同一个环内每把一个数移动到目标位置都会使环的长度减一,总共需要交换 L1 次。因此最终答案就是 n 减去环的个数。
注意本题没有要求只能对两相邻分量进行交换,因此不用计算逆序对数,另外内积会超过 int 范围,需用 long long 型数据。

©️2020 CSDN 皮肤主题: 技术黑板 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值