HONG KONG OLYMPIAD IN INFORMATICS · 2026/27

初级组初赛
逐题答案与解题过程

根据《HH CO 30.pdf》的印刷正文独立解答。覆盖卷一 K 版,以及卷二 O 版 Python 部分;含 31 道题、全部小问和代码填空。

中文解析可离线阅读含推导与代码核验

阅读说明与扫描范围

这是独立题解,不是官方答案表。仅以扫描件中的印刷题干、代码和图形连线为依据;铅笔圈选、勾叉、手写数字、草稿及对图形的涂画均不作为已知条件或答案来源。原始 PDF 未作修改。

文件共 23 页:第 1–12 页为卷一封面及 K-1 至 K-11;第 13–23 页为卷二封面及 Python 的 O-1 至 O-8,其中 PDF 第 19、20 页分别重复第 17、18 页。没有卷二 C++ 题页,因此不猜补未提供题目。卷一同时印有 Python/C++ 的共通题,按其共同语义解析。

题干在下文作忠实摘要,代码与关键数据按印刷正文整理。答案栏给出一种有效填法,代码填空可能有等价写法。扫描件没有答案纸,无法核对每个填空的格数上限;下列填法已尽量简短。页码同时列出印刷页码及 PDF 页序,便于回查。

来源日期为试卷印刷的 2026 年 11 月 7 日。网页无外部脚本、字体或网络依赖。

答案总表

点题号跳转。所有答案均在下方附有推导;代码填空按空格标签列出。

卷一 · 甲部

题号内容答案
卷一 甲1扫描数组找最大值C · 5
卷一 甲2队列与栈的操作顺序B · 3
卷一 甲3字符串的字典序D · 只有 i
卷一 甲4必要条件与逆否命题D · 只有 ii
卷一 甲5逐行求和应在哪里清零D · 在第 4 与第 5 行之间清零
卷一 甲6最少区间覆盖A · 3 次
卷一 甲7稳定排序与主次关键字C
卷一 甲8三台工作站的最优配对B · 12 分钟
卷一 甲9由相邻最大值还原排列B · 12 种
卷一 甲10翻转操作的奇偶不变量D · 50 个 N

卷一 · 乙部

题号内容答案
卷一 乙1复合赋值与取余A = 2026
卷一 乙2下标为奇数或值为奇数B = 11
卷一 乙3五天八根香蕉的限制计数C = 7
卷一 乙4循环终止区间与边界D = 125
卷一 乙5从新最大值和新最小值反推排列E = 4,2,3,5,6,1
卷一 乙6四种运算的最大结果F = 30

卷一 · 丙部

题号内容答案
卷一 丙1判断十进制中恰有一个非零位G = n%10;H = n<10;I = n%p
卷一 丙2会变色的三角形落球板J = 6;K = 8;L = 6

卷二 Python · 甲部

题号内容答案
卷二 Python 甲1字符串拼接与整数相加B · 1234,46
卷二 Python 甲2依次赋值交换变量C · 8 5
卷二 Python 甲3带条件的列表推导式A · 30
卷二 Python 甲4取模实现循环下标D · 21
卷二 Python 甲5二维列表重复产生的共享引用C · 7

卷二 Python · 乙部

题号内容答案
卷二 Python 乙1双重取余的周期A = 24
卷二 Python 乙2原地更新与反向下标B = 5138428
卷二 Python 乙3异或消去与十轮周期C = 23
卷二 Python 乙4字母 I、O 转为整数D = x==I
卷二 Python 乙5按给定输入输出表补条件E = s[:2]!="ca"
卷二 Python 乙6用成员关系异或求对称差F = (x in s)!=(x in t)

卷二 Python · 丙部

题号内容答案
卷二 Python 丙1判断非零整数是否全部同号G = pos==n or neg==n;H = a[0];I1 = a[i];I2 = val*val
卷二 Python 丙2移动一个元素到末尾J = a[i+1];K1 = a[i+1]!=b[i];K2 = a[p]==b[n-1]
显示 31 / 31 道题

卷一 · 甲部

返回总表 ↑卷一 甲1

扫描数组找最大值

原题:K-2 · PDF 第 3 页

题意:数组为 [3, 1, 4, 5, 2],ans 从 0 开始;遇到比 ans 大的元素就更新 ans。输出什么?

答案:C · 5
解题过程
a = [3, 1, 4, 5, 2]
ans = 0
for i in range(5):
    if a[i] > ans:
        ans = a[i]
print(ans)
读到的元素更新后的 ans
33
13
44
55
25

循环始终保存已经读过的元素中的最大值;最后输出 5。

返回总表 ↑卷一 甲2

队列与栈的操作顺序

原题:K-2 · PDF 第 3 页

题意:Q 是队列,S 是栈,最初均为空。依题目顺序入队、入栈,并在两者之间转移元素,求最后弹出 Q 的元素。

答案:B · 3
解题过程

队列先进先出,栈后进先出。下表的队列左端为队首,栈右端为栈顶。

操作Q(队首 → 队尾)S(栈底 → 栈顶)
1 入 Q[1][]
2 入 S[1][2]
3 入 Q[1, 3][2]
4 入 S[1, 3][2, 4]
S 弹出 4,放入 Q[1, 3, 4][2]
Q 弹出 1,放入 S[3, 4][2, 1]
Q 再弹出并输出[4][2, 1]

最后弹出的是 3。

返回总表 ↑卷一 甲3

字符串的字典序

原题:K-3 · PDF 第 4 页

题意:函数 f(x, y) 返回字符串比较 x < y。判断 i: f("0.11", "0.9");ii: f("six", "seven");iii: f("30", "100")。

答案:D · 只有 i
解题过程
比较第一个不同的字符结果
"0.11" < "0.9"'1' < '9'True
"six" < "seven"'i' > 'e'False
"30" < "100"'3' > '1'False

字符串逐字符比较,不能把它们当作数字大小或英文数词的数值比较。

返回总表 ↑卷一 甲4

必要条件与逆否命题

原题:K-3 · PDF 第 4 页

题意:Alice 只有在看见北极星时才会观星;多云则看不见北极星。已知今晚看得见北极星。i「Alice 今晚观星」与 ii「今晚非多云」,哪些必然成立?

答案:D · 只有 ii
解题过程

设 A 表示外出观星,P 表示看见北极星,C 表示多云。题设是 A ⇒ P 和 C ⇒ ¬P。

  1. 由第二条的逆否命题得到 P ⇒ ¬C。今晚 P 为真,故今晚非多云,ii 成立。
  2. 第一条只给出观星的必要条件。即使看得见北极星,Alice 也可能留在家中,所以 i 不一定成立。

不能把 A ⇒ P 直接反过来用成 P ⇒ A。

返回总表 ↑卷一 甲5

逐行求和应在哪里清零

原题:K-4 · PDF 第 5 页

题意:二维数组为 [[2, 4, 1], [5, 0, 2]]。原程序把 row_sum 初始化在外层循环之前。怎样修改才能输出 7 7?

答案:D · 在第 4 与第 5 行之间清零
解题过程
a = [[2, 4, 1], [5, 0, 2]]
row_sum = 0
for row in range(2):
    row_sum = 0  # 选项 D:进入每一行时清零
    for col in range(3):
        row_sum += a[row][col]
    print(row_sum, end=" ")

两行的和分别是 2+4+1=7、5+0+2=7。原程序没有在换行时清零,会输出 7 14。

选项 A 把累加改成赋值,最后只留下每行末项 1、2;B 只改变输出的除法,得到 2、4;C 在内层每一列都清零,也只留下末项。因此应选 D。

C++ 对应插入 row_sum = 0;,位置也是外层循环内、内层循环前。

返回总表 ↑卷一 甲6

最少区间覆盖

原题:K-4 · PDF 第 5 页

题意:检查点为 1, 2, 5, 6, 7, 10, 11, 15。一次扫描可覆盖整数起点 x 到 x+4(两端包含),求最少扫描次数。

答案:A · 3 次
解题过程
  1. 从最左侧未覆盖的点开始:[1,5] 覆盖 1、2、5。
  2. 下一个未覆盖点为 6:[6,10] 覆盖 6、7、10。
  3. 下一个未覆盖点为 11:[11,15] 覆盖 11、15。

三次可行。又因为 1、6、11 两两相差至少 5,而同一扫描内两点的距离至多为 4,所以这三个点必须分属三次扫描。下界与构造相等,最优答案为 3。

返回总表 ↑卷一 甲7

稳定排序与主次关键字

原题:K-5 · PDF 第 6 页

题意:记录按分数降序排列,同分时按姓名升序排列。哪一种两轮排序方法一定正确?

答案:C
解题过程

先按次关键字「姓名升序」排好,再按主关键字「分数降序」作稳定排序。

第二轮决定整体的分数顺序;对同分记录,它保留第一轮已经排好的姓名顺序。这正是选项 C。

A 保留的是姓名降序;B、D 最后一轮按姓名排序,会把姓名变成主关键字,不符合要求。

records.sort(key=lambda r: r.name)
records.sort(key=lambda r: r.score, reverse=True)

以上是 Python 稳定排序的示意;也可以直接用复合关键字 (-score, name)。

返回总表 ↑卷一 甲8

三台工作站的最优配对

原题:K-5 · PDF 第 6 页

题意:六项工作分别需要 2、3、4、7、8、10 分钟。三台工作站同时开始,每台必须依次处理恰好两项工作,求全部完成的最早时间。

答案:B · 12 分钟
解题过程

把最长和最短的工作配对,可取:

工作站两项任务完成时间
110 + 212
28 + 311
37 + 411

于是 12 分钟全部完成。任何方案中,10 分钟的工作都必须与另一项配对,而最短另一项也要 2 分钟,故总完成时间至少为 12。构造达到下界。

返回总表 ↑卷一 甲9

由相邻最大值还原排列

原题:K-6 · PDF 第 7 页

题意:1 至 6 各出现一次。五个相邻位置的较大值依次为 4、4、6、6、5,求可能的排列数。

答案:B · 12 种
解题过程

设六个位置为 a₁,…,a₆。

  1. 前两个读数均为 4。由于 4 只出现一次,必须在两对位置的交点,即 a₂=4。
  2. 中间两个读数均为 6,同理 a₄=6。
  3. 最后读数为 5,故 a₅、a₆ 中恰有一个为 5,有 2 种选位。
  4. 剩余三个位置放入 1、2、3,任意排列都满足其余上界,有 3!=6 种。

总数为 2×6=12。两类结构是 _ 4 _ 6 _ 5 与 _ 4 _ 6 5 _。已穷举 6!=720 个排列核验。

返回总表 ↑卷一 甲10

翻转操作的奇偶不变量

原题:K-6 · PDF 第 7 页

题意:长度 N 的二进制串初始全 0。一次操作选定一个位置,翻转其余所有位。对 1≤N≤100,有多少个 N 能变成全 1?

答案:D · 50 个 N
解题过程

N 为奇数时不可能:每次翻转 N−1 个位,这是偶数个位。因此 1 的个数的奇偶性始终保持为偶数;目标全 1 含奇数个 1,不可达。这也包含 N=1。

N 为偶数时可以:把每个位置都选一次,总共 N 次。任意一个位只在选择自己时不翻转,合计翻转 N−1 次;该数为奇数,所以每个位最终都是 1。

恰好是 2、4、…、100,共 50 个。

卷一 · 乙部

返回总表 ↑卷一 乙1

复合赋值与取余

原题:K-7 · PDF 第 8 页

题意:a=2026,b=27,依次执行 b *= a * b、b %= a,再输出 a+b。

答案:A = 2026
解题过程
a = 2026
b = 27
b *= a * b
b %= a
print(a + b)

b *= a * b 等价于把旧 b 乘上 a * 旧b,所以 b 变成 2026×27²=1,476,954。它是 a 的整数倍,取余后 b=0,输出 2026。

返回总表 ↑卷一 乙2

下标为奇数或值为奇数

原题:K-7 · PDF 第 8 页

题意:a=[4,5,3,1,0,2]。若 i%2==1 或 a[i]%2==1,则把 a[i] 加到 ans,求最终 ans。

答案:B = 11
解题过程
ia[i]i 为奇数?值为奇数?是否累加
04否否否
15是是是
23否是是
31是是是
40否否否
52是否是

累加 5+3+1+2=11。逻辑「或」允许任意一个条件成立,但两个条件都成立时仍只加一次。

返回总表 ↑卷一 乙3

五天八根香蕉的限制计数

原题:K-7 · PDF 第 8 页

题意:星期一到星期五每天至少吃一根香蕉,共吃八根;任意连续两天的总数不能为 3。求有序序列数。

答案:C = 7
解题过程

先每天放 1 根,剩余 3 根按整数分拆分类。相邻和为 3 恰好对应相邻出现 1,2 或 2,1。

  1. 额外的 3 根全放一天:得到一个 4 和四个 1。4 放哪一天都行,共 5 种。
  2. 额外根数按 2+1 分配:得到一个 3、一个 2 和三个 1。2 不能邻接 1,只能放在一端,并让唯一邻居为 3。因此只有 (2,3,1,1,1) 和 (1,1,1,3,2),共 2 种。
  3. 额外根数按 1+1+1 分配:得到三个 2 和两个 1。一行中同时有 1 和 2,必存在相邻交界,所以无效。

总数为 5+2=7。完整列表:

1, 1, 1, 1, 4
1, 1, 1, 3, 2
1, 1, 1, 4, 1
1, 1, 4, 1, 1
1, 4, 1, 1, 1
2, 3, 1, 1, 1
4, 1, 1, 1, 1
返回总表 ↑卷一 乙4

循环终止区间与边界

原题:K-8 · PDF 第 9 页

题意:整数输入 0≤x≤10000。cnt=0、i=1;当 cnt²≤x 时执行 cnt+=i、i+=1,最后输出 cnt。多少个 x 会输出 15?

答案:D = 125
解题过程
cnt = 0
i = 1
while cnt * cnt <= x:
    cnt += i
    i += 1
print(cnt)

cnt 的取值依次是三角数 0 → 1 → 3 → 6 → 10 → 15 → 21 → …。

  1. 必须能从 10 走到 15,故 10²≤x,即 x≥100。
  2. 到达 15 后必须停止,故 15²>x,即 x≤224。

所以 x∈[100,224],个数为 224−100+1=125。x=225 会继续走到 21,不计入。

返回总表 ↑卷一 乙5

从新最大值和新最小值反推排列

原题:K-8 · PDF 第 9 页

题意:1 至 6 的排列从第二项开始:大于此前所有值输出 L,小于此前所有值输出 S,其余输出 -。已知输出为 S-LLS,求排列。

答案:E = 4,2,3,5,6,1
解题过程
  1. 最后一项为新最小值,所以必须是 1。
  2. 第五项为新最大值,而 1 在最后,所以第五项必须是 6。
  3. 第四项也为新最大值,剩余的 5 必须出现在第四项,故第四项为 5。
  4. 前三项只能是 2、3、4。第二项比第一项小,第三项既不创新高也不创新低,故第三项夹在前两项之间。唯一顺序为 4,2,3。
项值与此前元素比较输出
22小于 4S
33介于 2 与 4 之间-
45大于此前全部L
56大于此前全部L
61小于此前全部S

答案 4,2,3,5,6,1,已穷举确认唯一。

返回总表 ↑卷一 乙6

四种运算的最大结果

原题:K-9 · PDF 第 10 页

题意:N 初始为 5。把 +、−、×、÷ 各用一次,操作数 1、2、3、4 也各用一次;逐步从左到右运算,除法为精确数学除法,求最大结果。

答案:F = 30
解题过程

可行构造:5 →(+3) 8 →(×4) 32 →(−2) 30 →(÷1) 30。

证明不能更大:设加数为 a,减数为 b,乘数为 m,除数为 d。所有操作数均为正数。

  1. 若 d=1,除法无影响。加法放在乘法前可使它被放大,减法放在乘法后可使扣除最少。因此结果至多 m(5+a)−b。a、b、m 是 2、3、4 的排列,最大值在 m=4、a=3、b=2 时取得,为 30。
  2. 若 d≥2,初始 5 对最终值的贡献恒为 5m/d;加数 a 的贡献至多为 am;减数的贡献始终为负,忽略它只会提高上界。因此结果≤5m/d+am≤10+12=22,小于 30。这里 m≤4,且不同操作数的乘积 am≤4×3=12。

故最大值为 30。另用精确分数穷举 4!×4!=576 种方案核验,避免浮点误差。

from itertools import permutations
from fractions import Fraction

best = Fraction(-10**9)
for ops in permutations("+-*/"):
    for nums in permutations([1, 2, 3, 4]):
        v = Fraction(5)
        for op, n in zip(ops, nums):
            if op == "+": v += n
            elif op == "-": v -= n
            elif op == "*": v *= n
            else: v /= n
        best = max(best, v)
print(best)  # 30

卷一 · 丙部

返回总表 ↑卷一 丙1

判断十进制中恰有一个非零位

原题:K-10 · PDF 第 11 页

题意:1≤n≤10⁹。分别用三种方法补全函数,使其当且仅当 n 恰有一个非零十进制数位时返回 True。

答案:G = n%10;H = n<10;I = n%p
解题过程

(a) G:逐位统计非零位

def f(n: int) -> bool:
    c = 0
    while n > 0:
        if n % 10 != 0:  # G = n%10
            c += 1
        n //= 10
    return c == 1

n%10 取个位,n//=10 去掉个位,最终 c 就是非零位个数。

(b) H:去掉末尾的全部 0

def g(n: int) -> bool:
    while n % 10 == 0:
        n //= 10
    return n < 10  # H = n<10

若原数形如 d×10ᵏ(1≤d≤9),去掉所有末尾 0 后只剩一位。反之,剩下两位或更多,且个位非零,就至少有两个非零位。题设 n≥1,因此无需处理 0 的死循环。

(c) I:检查最高位以下的余数

def h(n: int) -> bool:
    p = 1
    while n // p >= 10:
        p *= 10
    return n % p == 0  # I = n%p

循环结束后,p 是不超过 n 的最大 10 的幂。例如 n=6000 时 p=1000。n%p=0 当且仅当最高位以下全为 0。单个位数时 p=1,余数也为 0,符合要求。

C++ 的三个填空与 Python 相同,依次为 n%10、n<10、n%p;原 C++ 框架的整数除法使用 / 和 /=。

返回总表 ↑卷一 丙2

会变色的三角形落球板

原题:K-11 · PDF 第 12 页

题意:四层三角形圆圈最初全白,白色往左、黑色往右,球离开后颜色翻转。X 是第三层中间圆圈,出口编号 0 至 4;各小题独立重置。

答案:J = 6;K = 8;L = 6
解题过程

(a) J:最先经过 X 的 11 个球中,向左几个?

X 初始白色,只在球经过它时翻色,所以从 X 出发的方向交替为 L、R、L、R、…。前 11 次经过有 6 次向左。这问的是「经过 X 的前 11 个球」,不是「总共落下的前 11 个球」。

(b) K:前 16 个球中,几个经过 X?

顶点把 16 个球均分成 8、8;第二层两个点又各分成 4、4,因此第三层各点接收 4,8,4。中间 X 接收 8 个。

(c) L:前 16 个球中,几个从出口 2 离开?

第三层的 4、8、4 均为偶数,各自均分后,第四层接收 2,6,6,2。第四层再均分,五个出口收到 1,4,6,4,1。所以出口 2 收到 6 个。

下表给出逐球模拟作交叉核验,L/R 分别表示左/右:

球编号四层路径出口
1LLLL0
2RLLL1
3LRRL2
4RRLR3
5LLRR2
6RLLL1
7LRRL2
8RRRL3
9LLLR1
10RLLR2
11LRRR3
12RRLL2
13LLRL1
14RLLR2
15LRRR3
16RRRR4
state = [[0] * (r + 1) for r in range(4)]
exits = [0] * 5
hits_x = 0
for _ in range(16):
    c = 0
    for r in range(4):
        if r == 2 and c == 1:
            hits_x += 1
        direction = state[r][c]  # 0 左,1 右
        state[r][c] ^= 1
        c += direction
    exits[c] += 1
print(hits_x)  # 8
print(exits)   # [1, 4, 6, 4, 1]

卷二 Python · 甲部

返回总表 ↑卷二 Python 甲1

字符串拼接与整数相加

原题:O-2 · PDF 第 15 页

题意:程序 (1) 直接拆分 "12 34" 后输出 a+b;程序 (2) 拆分后用 map(int, ...) 转换,再输出 a+b。

答案:B · 1234,46
解题过程
# 程序 (1)
a, b = "12 34".split()
print(a + b)  # 1234

# 程序 (2)
a, b = map(int, "12 34".split())
print(a + b)  # 46

split() 得到两个字符串,字符串的 + 是拼接;int 转换后,+ 才是数值加法。

返回总表 ↑卷二 Python 甲2

依次赋值交换变量

原题:O-2 · PDF 第 15 页

题意:x=5,y=8;依次执行 x=x+y、y=x−y、x=x−y,最后 print(x,y)。

答案:C · 8 5
解题过程
状态xy
初始58
x = x + y138
y = x - y135
x = x - y85

每一步都使用当前值,所以结果是 8 5。print 的默认分隔符是一个空格。

返回总表 ↑卷二 Python 甲3

带条件的列表推导式

原题:O-3 · PDF 第 16 页

题意:a=[i+2 for i in range(10) if i%2==0],输出 sum(a)。

答案:A · 30
解题过程

range(10) 给出 0 至 9;过滤后 i 为 0,2,4,6,8,各加 2 得到 [2,4,6,8,10]。

总和为 2+4+6+8+10=30。注意 range(10) 不包含 10,但计算后的元素可以等于 10。

返回总表 ↑卷二 Python 甲4

取模实现循环下标

原题:O-3 · PDF 第 16 页

题意:a=[1,2,3,4,5,6,7],idx=0、ans=0。重复五次:累加 a[idx],再把 idx 更新为 (idx+3)%len(a)。

答案:D · 21
解题过程
第几轮累加前 idxa[idx]累计 ans更新后 idx
10113
23456
367122
423155
556211

累加的是 1+4+7+3+6=21。下标从 0 开始,并且先累加、后更新。

返回总表 ↑卷二 Python 甲5

二维列表重复产生的共享引用

原题:O-3 · PDF 第 16 页

题意:a=[[0]*6]*7;执行 a[0][0]+=1 后,用两层循环累加全部 7×6 个位置,输出什么?

答案:C · 7
解题过程
a = [[0] * 6] * 7
a[0][0] += 1
s = 0
for i in range(7):
    for j in range(6):
        s += a[i][j]
print(s)  # 7

外层的 *7 复制的是对同一个内层列表的引用。因此 a[0] 到 a[6] 都指向同一行;修改 a[0][0] 后,每一行看上去都是 [1,0,0,0,0,0]。累加时这一行被遍历 7 次,故结果为 7。

这不是把全部 42 个位置都改成 1。若需要 7 个独立的行,应写 [[0]*6 for _ in range(7)];但本题必须按原程序解读。

卷二 Python · 乙部

返回总表 ↑卷二 Python 乙1

双重取余的周期

原题:O-4 · PDF 第 17 页;第 19 页为重复扫描

题意:ans=0;对 i in range(20),把 (i%5)%4 加到 ans,输出 ans。

答案:A = 24
解题过程

i%5 的周期是 0,1,2,3,4,再取 %4 后的周期为 0,1,2,3,0,每周期和为 6。

0 至 19 恰有 4 个完整周期,总和为 4×6=24。不能把 (i%5)%4 简化成 i%4。

返回总表 ↑卷二 Python 乙2

原地更新与反向下标

原题:O-4 · PDF 第 17 页;第 19 页为重复扫描

题意:a=[2,0,2,4,1,1,3];按 i=0,…,6 执行 a[i]+=a[6−i];随后依次输出各元素,end="",中间不加分隔符。

答案:B = 5138428
解题过程

数组在原地更新。后半段读取前半段时,读到的是已经改过的值。

i此次计算更新后的数组
02+3=5[5, 0, 2, 4, 1, 1, 3]
10+1=1[5, 1, 2, 4, 1, 1, 3]
22+1=3[5, 1, 3, 4, 1, 1, 3]
34+4=8[5, 1, 3, 8, 1, 1, 3]
41+3=4[5, 1, 3, 8, 4, 1, 3]
51+1=2[5, 1, 3, 8, 4, 2, 3]
63+5=8[5, 1, 3, 8, 4, 2, 8]

最终列表为 [5,1,3,8,4,2,8],拼接输出是 5138428。

返回总表 ↑卷二 Python 乙3

异或消去与十轮周期

原题:O-4 · PDF 第 17 页;第 19 页为重复扫描

题意:a=[7,13,2,18,3]、x=0。i 从 0 到 2026:偶数轮 x^=a[i%5],奇数轮 x^=a[4−i%5]。最后输出 x。

答案:C = 23
解题过程

Python 的 ^ 是按位异或。满足 v^v=0、v^0=v,并具有交换律和结合律。

下标同时依赖 i%2 与 i%5,故以 10 为周期。前 10 轮的下标为:

0, 3, 2, 1, 4, 4, 1, 2, 3, 0

每个下标恰好出现两次,所以一个完整周期的异或为 0。2027=202×10+7,只需看最后 7 轮,其下标为 0,3,2,1,4,4,1。

两次 4 和两次 1 抵消,剩下 a[0]^a[3]^a[2]=7^18^2。二进制为:

00111  (7)
10010  (18)
00010  (2)
-----
10111  (23)

结果为 23,不是乘方运算。

返回总表 ↑卷二 Python 乙4

字母 I、O 转为整数

原题:O-5 · PDF 第 18 页;第 20 页为重复扫描

题意:字符串 s 只含大写字母 I 和 O。框架中 I='I'、O='O',在 t.append(int( D )) 的 D 处填入表达式,使 I 转成整数 1、O 转成整数 0。

答案:D = x==I
解题过程
def convert(s: str) -> list[int]:
    n = len(s)
    t = []
    I = 'I'
    O = 'O'
    for x in s:
        t.append(int(x == I))  # D = x==I
    return t

x 为字母 I 时,比较结果为 True,int(True)=1;x 为字母 O 时,比较结果为 False,int(False)=0。例如 convert("IOI") 得到 [1,0,1]。

印刷题目使用的是字母 I 和 O;不是数字 1 和 0。直接 int(x) 会报错,字符串之间的按位 & 也不可用。

返回总表 ↑卷二 Python 乙5

按给定输入输出表补条件

原题:O-5 · PDF 第 18 页;第 20 页为重复扫描

题意:carrot、cabbage 应输出 uhh;coconut、cheeseburger、cookies、curry、clams 应输出 yummy。在 print("yummy" if E else "uhh") 中补条件。

答案:E = s[:2]!="ca"
解题过程
s = input()
print("yummy" if s[:2] != "ca" else "uhh")

给定表中,恰好只有 carrot、cabbage 以 ca 开头。对它们条件为 False,走 else 分支;其余五项条件为 True,输出 yummy。

这是满足表中全部输入的一种答案,表达式不唯一,例如 not s.startswith("ca") 也满足给定表。题面没有定义表外单词应有的输出,不把这个前缀规则当成未给出的普遍规律。

返回总表 ↑卷二 Python 乙6

用成员关系异或求对称差

原题:O-6 · PDF 第 21 页

题意:s、t 各自没有重复元素。补全 [x for x in s+t if F],返回只属于其中一个列表的元素,顺序不限。

答案:F = (x in s)!=(x in t)
解题过程
def find_symmetric_difference(s: list, t: list) -> list:
    return [x for x in s + t if (x in s) != (x in t)]

两个布尔值不相等,恰好表示一真一假,也就是仅属于一个列表。

x 属于 sx 属于 t是否保留
是否是
否是是
是是否
否否否;且不会由 s+t 枚举到

列表各自无重复,所以仅在一边出现的元素也只会在 s+t 中出现一次。示例 s=[1,5,3]、t=[2,3,6] 得到 [1,5,2,6]。

两侧成员判断要保留括号,避免 Python 比较运算的链式解释。

卷二 Python · 丙部

返回总表 ↑卷二 Python 丙1

判断非零整数是否全部同号

原题:O-7 · PDF 第 22 页

题意:列表长度 1 至 1000,所有元素都是 −1000 至 1000 的非零整数。分别补全三种函数:仅当全正或全负时返回 True。

答案:G = pos==n or neg==n;H = a[0];I1 = a[i];I2 = val*val
解题过程

(a) G:统计正负个数

def check_same_sign1(a: list[int]) -> bool:
    n = len(a)
    pos = 0
    neg = 0
    for i in range(n):
        if a[i] > 0:
            pos += 1
        else:
            neg += 1
    return pos == n or neg == n  # G

全部为正或全部为负即可。等价的短写法是 pos*neg==0;题设保证非空且没有 0。

(b) H:与固定参照元素比符号

def check_same_sign2(a: list[int]) -> bool:
    n = len(a)
    val = 0
    for i in range(n):
        val += a[i] * a[0] > 0  # H = a[0]
    return val == n

两个非零数乘积为正,当且仅当同号。固定与 a[0] 比较,所有 n 次都为真即同号。布尔值 True 累加为 1。不能填 a[i],那样只是在检查每个数的平方是否为正,混合正负也会错误通过。

(c) I1、I2:累加符号

def check_same_sign3(a: list[int]) -> bool:
    n = len(a)
    val = 0
    for i in range(n):
        val += a[i] / abs(a[i])  # I1 = a[i]
    return val * val == n * n   # I2 = val*val

每项 a[i]/abs(a[i]) 等于 +1 或 −1。若正数有 p 个、负数有 q 个,val=p−q,n=p+q;只有 p=0 或 q=0 时,才有 val²=n²。

原框架这里是普通除法 /;本题商只有 ±1,且 n≤1000,累加结果可以精确表示,不影响平方比较。I1 与 I2 必须同时正确。

返回总表 ↑卷二 Python 丙2

移动一个元素到末尾

原题:O-8 · PDF 第 23 页

题意:一次操作把列表中某个位置的元素移到末尾,其他元素相对顺序不变。(a) 补全移动函数;(b) 判断等长列表 b 能否由 a 至多操作一次得到。

答案:J = a[i+1];K1 = a[i+1]!=b[i];K2 = a[p]==b[n-1]
解题过程

(a) J:将后续元素依次左移

def move(a: list[int], p: int) -> None:
    n = len(a)
    x = a[p]
    for i in range(p, n - 1):
        a[i] = a[i + 1]  # J = a[i+1]
    a[n - 1] = x

先保存 a[p],再把后面所有元素左移一格,最后把保存的 x 放到末尾。例如 [4,7,4,2] 的 p=1,得到 [4,4,2,7]。

(b) K1、K2:找首个不匹配位置

def can_obtain(a: list[int], b: list[int]) -> bool:
    n = len(a)
    p = 0
    while p < n and a[p] == b[p]:
        p += 1
    if p == n:
        return True
    for i in range(p, n - 1):
        if a[i + 1] != b[i]:  # K1
            return False
    return a[p] == b[n - 1]   # K2
  1. 如果全部相等,零次操作即可,直接返回 True。
  2. 否则设 p 为首个不匹配的位置。候选操作是把 a[p] 移到末尾。这样 b[p] 至 b[n−2] 应等于 a[p+1] 至 a[n−1];任何不等都应拒绝,所以 K1 使用 !=。
  3. 末尾必须等于被移走的 a[p],所以 K2 为 a[p]==b[n-1]。

为什么有重复值时也能只检查 p?若某个可行操作移走了更早的 q<p,那么 q 到 p−1 这一段在移动后仍与原列表相等。这迫使 a[q]=a[q+1]=…=a[p]。从这一段中改为移走 a[p] 会得到相同结果,所以检查 p 不会漏解。若 q>p,则位置 p 根本不会改变,不可能修复首个不匹配。

算法时间 O(n),额外空间 O(1)。已对元素来自 {0,1,2}、长度 0 至 5 的全部 66,430 对等长列表与「枚举每个可移动位置」的结果交叉核验,包含重复元素、无需移动和单元素情形。

核验与易错点

先从印刷题干手工推导,再用本地 Python 执行或穷举交叉检查。核验不是官方评分结果,也不替代对扫描题意的辨读。

  • 组合与边界:卷一甲 9 穷举 720 个排列;乙 3 枚举所有满足总和条件的正整数序列;乙 4 检查 0 至 10000 全部输入;乙 5 穷举 720 个排列;乙 6 用精确分数比较全部 576 种运算顺序与操作数分配。
  • 状态模拟:卷一丙 2 逐球模拟,与逐层分流计算一致;卷二原地数组修改、共享列表引用、2027 轮异或均直接执行核对。
  • 代码填空:卷一三个数位判断函数对 1 至 100000 逐个核验,并以推导覆盖题设上限;卷二同号判断对元素取自 {−2,−1,1,2}、长度 1 至 6 的全部列表核验;移到末尾判断对小规模全部 66,430 对等长列表与枚举操作交叉检查。
  • 边界限定:以上程序核验使用本地 Python,未复现题面指定的 Debian 12 / Python 3.11.2 / G++ 12.2.0 整套环境,也未将每个 C++ 程序重新编译;卷一跨语言结论来自题面代码的共同语义。

重点复习:必要条件不能倒推;while 条件含等号时注意退出边界;列表重复可能共享引用;原地修改会影响后续读取;位运算 ^ 不是乘方;图上白色圆圈第一次走左支;固定参照比较符号,不能用自己乘自己。