今天进行了两次测试,以下是对这两次比赛的总结:
第一场
T1
题目:
有一张无限大的棋盘(可以认为是一个无限大的方格平面)。
现在棋盘上放了 k 个棋子,第 i 个棋子位于坐标 (ri,ci)(行、列坐标均为整数)。
如果两枚棋子满足
∣r1−r2∣=∣c1−c2∣
则认为它们可以相互攻击。
请你计算:一共有多少对棋子可以相互攻击。
思路:
这个条件的式子可以去掉绝对值后移项,就能得到:
1.r1-c1=r2-c2
2.r1+c1=r2+c2
那么只需将每个点的r+c和r-c的值放到两个map里面,统计值相同的对数即可。
T2
题目:
给定一个长度为 n 的序列 a1,a2,...,an。
定义答案为所有无序数对 (i,j)(1≤i<j≤n)的差的平方之和:
1≤i<j≤n∑(ai−aj)2
由于答案可能非常大,请输出答案对 1000000007 取模后的结果。
思路:
求所有无序数对的差的平方之和,我们不妨拆分一下:
首先看平方项。每一个有i的无序对都有1个ai^2,所有的加在一起就是(n-1)*ai^2;再看乘积项。2*ai*aj可以分到i和j上,每个有一个ai*aj。以ai举例,最后能统计出是除了ai的所有数与ai的乘积的和。根据运算律可得出是(a1到an的总和-ai)*ai,以此类推即可。当然,要注意数值范围(涉及到乘3次)。
T3
题目:
给定若干个正整数 n,你可以对每个 n 分别进行操作,直到它变成 1。
每次操作你可以选择:
- 让 n 变为 n−1;
- 选择 n 的一个质因数 p(即 p 是质数且 p∣n),让 n 变为 n/p。
请你计算:对每个输入的 n,最少需要多少次操作可以把它变成 1。
思路:
在线求是不好求的。我在这里使用的是离线处理,使用dp,从小到大。
当我们要求dp[n]时,我们可以拆分成2种:1.变成n-1处理;2.变成n/p处理。所以转移是这样的:
dp[i]=dp[i-1];
dp[i]=min(dp[i],dp[i/p]);
T4
题目:
给定一个长度为 n 的序列 a1,a2,...,an,其中每个 ai 只可能是 −1,0,1。
对任意一个非空子数组 [l,r],定义它的区间乘积为:
al×al+1×⋯×ar
请你分别统计:
- 区间乘积为负数的子数组数量;
- 区间乘积为正数的子数组数量;
- 区间乘积为 0 的子数组数量。
思路:
如果子数组里面有0,那么乘积一定为0.所以我们可以直接把大数组拆成几个以0为分界线的小数组。那么小数组之内又怎么求呢?
我们求乘积为负的。我们首先将前缀有偶数个负数和有奇数个负数的数量求出来,如果区间乘积为负数,则其中一定有奇数个负数。利用前缀和的原理,可以知道区间乘积为负数则有两种情况:1.本身;2.以前缀有偶数个负数和有奇数个负数为边界。
最后能得出计算式:sum偶*sum奇+sum奇。正数和0的数量相减计算即可得出。(长度为n的数组子数组数量为n*(n+1)/2)
T5
题目:
有 n 节课。第 i 节课的开始时间为 si,结束时间为 ei(保证 si<ei),上课时长为 ei−si。
你可以选择上其中的一部分课,但一旦选择某节课,就必须从开始一直上到结束。
已知你选择的任意两节课的时间段不能有交集(允许端点相等 ):两节课 (s1,e1) 与 (s2,e2) 可以同时被选择,当且仅当 e1≤s2 或 e2≤s1。 也就是说,上一节课的结束时间 可以等于 下一节课的开始时间。
请你计算:最多能上课的总时长是多少。
思路:
dp+贪心。以结束先后排序,每一节课都按选不选进行排序。如果不选就从前面一节课的选或不选中找最大(dp[i]的两种选择中肯定有当前最大值),选的话就要看那节课的结束时间最接近这节课的开始时间且不重叠,再从这一节课的选或不选中找最大即可。最后输出第n节课的选或不选中的最大
T6
题目:
给定三个牛奶桶 A、B、C,其容积分别为 v,x,y。初始状态下,三个桶中的牛奶量分别为 (v,0,0)。
你可以在任意两个桶之间进行"倾倒操作"。例如,从桶 S(源桶)向桶 D(目标桶)倒牛奶,规则如下:
- 如果 S 中的牛奶量大于或等于 D 桶剩余的容量,则将 D 倒满,S 中将剩下多余的牛奶。
- 如果 S 中的牛奶量小于 D 桶剩余的容量,则将 S 中的牛奶全部倒入 D。
注意:题目不允许从外部补充牛奶,也不允许把不想要的牛奶倒掉不要。
你的任务是计算出,要使 A、B、C 其中任意一个桶的牛奶量恰好为 2v,所需要的最少倾倒操作次数
思路:
直接进行一个bfs。因为数据范围小(v,x,y<200),所以直接bfs,将所有倾倒情况都加进去(没错,直接全部模拟一遍,虽然有点麻烦),最后输出结果即可。
第二场
T1
题目:
我们称一个非负整数为"纯偶数",当且仅当它的每一位数字都是偶数(只能出现 0,2,4,6,8)。
把所有纯偶数按从小到大排列为:
0,2,4,6,8,20,22,24,...
从 1 开始编号(即第 1 个是 0)。你的任务是输出第 n 个纯偶数。
思路:
因为所有的组成的数从0,1,2,3,4,5,6,7,8,9变成0,2,4,6,8,从10个数变成5个数,其实就相当于从10进制变成5进制,最后每一位再*2.当然,因为从 1 开始编号,所以别忘了先-1在转换。(虽然我并不是这么写的)
T2
题目:
给定一个长度为 n 的字符串 s(只包含大写字母)。
请你统计有多少个三元组 (i,j,k) 满足:
- 1≤i<j<k≤n;
- si=C、sj=S、sk=P。
也就是说,你需要统计字符串中有多少个子序列等于 "CSP"。
思路:
前缀和。sc[i]表示到s[i]有多少个'C',这个直接求就行。ss[i]表示到s[i]有多少个'CS',此时如果s[i]是'S',那么就要加前面所有的'C'的数量(因为都能组成)。最后在求sp[i],方法和ss[i]一样。最后输出sp[n]即可。
T3
题目:
有 n 个文件,第 i 个文件的大小是 ai。
你可以反复进行"合并压缩"操作:每次任选两个文件合并成一个新文件,合并代价等于这两个文件大小之和,新文件的大小也等于它们之和。
你想把这 n 个文件最终合并成一个文件。请输出最小总代价。
思路:
首先,要知道需要先合并当前最小的两个文件(同哈夫曼树),这里就不解释了。求最小可以用堆来解,在c++中为优先队列,当然要重载成小根堆。每次取出两个,计算和再放回去,最后输出总和即可。
T4
题目:
你需要处理 n 组询问。
每组询问给定一个整数 x,你需要找两个整数 a,b,满足:
- 0≤a,b≤x;
- a⊕b=x;
并使 a+b 尽可能大。请输出这个最大值。
其中 ⊕ 表示按位异或:对应二进制位不同则为 1,相同则为 0。
思路:
首先,要确定0和1都能怎么异或出来,0可以通过0,0和1,1,1可以通过1.0和0,1.因为要小于原数,所以在x从高位到低位的2个1之前的0都只能用0,0表示,而两个数一个少最高位的1,一个少第2个1后,剩余的0都能用1,1来表示,最后输出和即可。
T5
题目:
有一个 2×n 的矩形网格(2 行 n 列)。你需要在每个格子里填入下列三个数之一:
- 1000000007
- 9223372036854775783
- 1000000000000000003
要求任意两个相邻格子(上下或左右相邻)中的数字互质,即它们的最大公因数为 1。
请你计算一共有多少种填数方案。
由于方案数可能很大,请输出答案对 10007 取模后的结果。
思路:
(其实我根本不知道这道题为什么事2*3^n次幂,但根据样例猜出来了)
这一道题的答案是2*3^n,因为n较大,所以使用快速幂即可。
T6
题目:
给定一个包含 n 个点、m 条边的连通无向图。每条边的长度均为 1。
请你计算从 1 号点到 n 号点的最短路长度(经过的边数)。
思路:
这道题看似是最短路,但因为权值都是1,所以可以用bfs来做,是比较裸的一道题。
如果大家有其他想法的,可以补充。