备战蓝桥杯国赛【Day 16】

📌 写在前面 :今天的3道题全部来自第十四届蓝桥杯省赛/国赛真题 ,核心考点包括:DFS枚举门电路组合、Lucas定理优化异或金字塔、贪心策略处理异或消除。每道题我都会从暴力思路讲起,一步步推导到最优解,讲清楚"为什么能这样优化"。


📚 今日刷题清单

题号 题目 来源 类型 难度 核心考点
1 与或异或 蓝桥杯 DFS枚举 ⭐⭐⭐ 递归回溯、位运算、剪枝
2 异或金字塔 蓝桥杯 组合数学 ⭐⭐⭐⭐⭐ Lucas定理、组合数奇偶性、位运算
3 小小蓝的异或消除 蓝桥杯 贪心+堆 ⭐⭐⭐⭐ 优先队列、异或性质、贪心策略

一、与或异或 ⭐⭐⭐ DFS枚举+位运算

1.1 题目描述

小蓝有一张门电路的逻辑图,呈金字塔结构:

  • 第0层(顶层输入):In[0..4],共5个值
  • 第1层:4个门电路,每个接收上一层相邻两个值
  • 第2层:3个门电路
  • 第3层:2个门电路
  • 第4层(输出层):1个门电路,输出 Out

每个门电路可以是与门(&)、或门(|)、异或门(^) 中的一种。

已知输入为 In = [1, 0, 1, 0, 1],求有多少种不同的门电路组合方式,使得最终输出 Out = 1

1.2 关键思路:DFS枚举

暴力思路 :每个门有3种选择,共 3^10 个门(第1层4个+第2层3个+第3层2个+第4层1个=10个),总组合数 3^10 = 59049,完全可以枚举!

DFS设计

  • arr[i][j] 表示第 i 行第 j 个节点的值
  • arr[0] 是输入层,已知
  • arr[1][0] 开始,逐个确定每个门的类型和输出值
  • 当填到 arr[4][0](输出层)时,检查是否为1

递归顺序

  • 当前处理 arr[x][y],它由 arr[x-1][y]arr[x-1][y+1] 运算得到
  • 处理完 arr[x][y] 后:
    • 如果同行还有元素(x+y < 4),处理 arr[x][y+1]
    • 否则进入下一行,处理 arr[x+1][0]

1.3 推演验证

复制代码
输入: In = [1,0,1,0,1]

金字塔结构:
第0层: 1   0   1   0   1
       ↘↙ ↘↙ ↘↙ ↘↙
第1层:  ?   ?   ?   ?
       ↘↙ ↘↙ ↘↙
第2层:   ?   ?   ?
       ↘↙ ↘↙
第3层:    ?   ?
       ↘↙
第4层:     ? (Out)

DFS过程:
从arr[1][0]开始,尝试3种运算:
- &: 1&0 = 0
- |: 1|0 = 1  
- ^: 1^0 = 1

假设选|,arr[1][0]=1,继续处理arr[1][1](因为1+0<4)
arr[1][1]由arr[0][1]=0和arr[0][2]=1运算...
...
直到arr[4][0],检查是否为1,统计合法方案数

1.4 题解

python 复制代码
# 初始化金字塔结构
# arr[i][j] 表示第i行第j列的值
# 第0行有5个元素,第1行4个,...,第4行1个
arr = [[0] * (5 - i) for i in range(5)]
arr[0] = [1, 0, 1, 0, 1]  # 输入层

ans = 0  # 合法方案数

def dfs(x, y):
    """
    x: 当前行号 (1~4)
    y: 当前列号 (0~4-x)
    """
    global ans, arr
    
    # 到达输出层(第4行),检查输出是否为1
    if x == 5:
        if arr[4][0] == 1:
            ans += 1
        return
    
    # 尝试三种门电路:与(0)、或(1)、异或(2)
    for op in range(3):
        if op == 0:
            # 与门
            arr[x][y] = arr[x-1][y] & arr[x-1][y+1]
        elif op == 1:
            # 或门
            arr[x][y] = arr[x-1][y] | arr[x-1][y+1]
        else:
            # 异或门
            arr[x][y] = arr[x-1][y] ^ arr[x-1][y+1]
        
        # 决定下一个处理位置
        if x + y < 4:
            # 当前行还有未处理的元素,向右移动
            dfs(x, y + 1)
        else:
            # 当前行处理完毕,进入下一行
            dfs(x + 1, 0)

# 从第1行第0列开始DFS
dfs(1, 0)
print(ans)

复杂度 :时间 O(3^10) = O(59049),空间 O(5×5)(递归栈深度最多5层)

1.5 关键细节

坑点 说明
递归终止条件 x == 5 表示已经填完第4行(输出层),不是 x == 4
下一个位置判断 x + y < 4:第 x 行有 5-x 个元素,列号范围 04-x,所以最后一个元素的 y = 4-x,此时 x+y = 4。如果 x+y < 4,说明还没到最后一个
全局变量 ansarr 要用 global 声明,或者改用类封装
运算优先级 &、`

二、异或金字塔 ⭐⭐⭐⭐⭐ 组合数学+Lucas定理

2.1 题目描述

卓儿有一个异或金字塔:

  • 第1层(底层):a[0], a[1], ..., a[n-1]
  • 第2层:a[0]^a[1], a[1]^a[2], ..., a[n-2]^a[n-1]
  • 第3层:第2层相邻两个异或
  • ...
  • 第n层:只剩一个数,即答案

给定底层数组,求最顶部的数字。

数据范围n ≤ 10^5

2.2 关键思路:组合数+Lucas定理

暴力思路 :模拟金字塔构建,逐层计算。时间 O(n²),空间 O(n²)
超时原因n=10^5 时,n² = 10^10,完全不可接受。

核心观察 :顶层结果 = 底层元素的线性组合(异或意义下)

复制代码
第1层: a0, a1, a2, a3, ...
第2层: a0^a1, a1^a2, a2^a3, ...
第3层: (a0^a1)^(a1^a2)=a0^a2, (a1^a2)^(a2^a3)=a1^a3, ...
第4层: (a0^a2)^(a1^a3)=a0^a1^a2^a3, ...

规律 :第 k 层的每个元素,是底层某些元素的异或,系数由组合数决定!

具体地,顶层结果 = Σ C(n-1, i) * a[i](异或意义下,即系数模2)

异或的系数是0或1 :只有当 C(n-1, i)奇数 时,a[i] 才会出现在结果中。

Lucas定理C(m, k) 为奇数,当且仅当 (k & m) == k(即 k 的二进制位是 m 的二进制位的子集)

2.3 数学推导

Lucas定理回顾

对于质数 p,有:
C(m, k) ≡ Π C(m_i, k_i) (mod p)

其中 m_i, k_im, kp 进制表示。

p=2 时:

  • m, k 的二进制表示为 m = (m_t...m_1m_0)_2, k = (k_t...k_1k_0)_2
  • C(m, k) ≡ Π C(m_i, k_i) (mod 2)
  • C(0,0)=1, C(1,0)=1, C(1,1)=1, C(0,1)=0
  • 所以 C(m_i, k_i) = 0 当且仅当 m_i=0k_i=1

因此:C(m, k) 为奇数 ⟺ 对所有 i,不存在 m_i=0, k_i=1k 的二进制位是 m 的二进制位的子集 ⟺ (k & m) == k

2.4 推演验证

复制代码
输入: n=8, a=[2,10,5,12,9,5,1,5]

mask = n-1 = 7 = 111₂

检查每个i:
i=0: 000 & 111 = 000 == 0 ✅ → a[0]=2
i=1: 001 & 111 = 001 == 1 ✅ → a[1]=10
i=2: 010 & 111 = 010 == 2 ✅ → a[2]=5
i=3: 011 & 111 = 011 == 3 ✅ → a[3]=12
i=4: 100 & 111 = 100 == 4 ✅ → a[4]=9
i=5: 101 & 111 = 101 == 5 ✅ → a[5]=5
i=6: 110 & 111 = 110 == 6 ✅ → a[6]=1
i=7: 111 & 111 = 111 == 7 ✅ → a[7]=5

result = 2^10^5^12^9^5^1^5 = 9 ✓

为什么n=8时所有i都满足?

因为 n-1=7=111₂,而 i 最大是 7=111₂,所以 i 的所有二进制位都不会超过 7 的位,(i & 7) == i 恒成立。

反例n=6mask=5=101₂

  • i=2=010₂: 010 & 101 = 000 != 2
  • i=3=011₂: 011 & 101 = 001 != 3

所以 a[2]a[3] 不会出现在结果中。

2.5 题解

python 复制代码
n = int(input())
a = list(map(int, input().split()))

result = 0
mask = n - 1  # 对应 Lucas 定理中的 m = n-1

for i in range(n):
    # Lucas定理:C(n-1, i) 为奇数 ⟺ (i & (n-1)) == i
    if (i & mask) == i:
        result ^= a[i]  # 系数为1,异或进结果

print(result)

复杂度 :时间 O(n),空间 O(1)(除输入数组外)

2.6 关键细节

坑点 说明
Lucas定理条件 C(m,k) 为奇数 ⟺ (k & m) == k,这里 m = n-1
mask的设定 mask = n - 1,不是 n。因为金字塔有 n 层,从底层到顶层经过 n-1 次运算
位运算优先级 i & mask 的括号可以省略(& 优先级高于 ==),但加上更清晰
为什么O(n)能过 利用了组合数学性质,避免了 O(n²) 的模拟
适用范围 只有当运算满足结合律交换律(如异或、加法模2)时才适用

2.7 本质理解

复制代码
异或金字塔的递推,本质上是在做:
result = Σ a[i] * C(n-1, i)  (mod 2)

因为:
- 异或 = 加法模2
- 每个a[i]出现的次数 = 从底层到顶部的路径数 = C(n-1, i)
- 模2后,只有奇数次出现才保留

这个思想可以推广到其他满足结合律的运算!


三、小小蓝的异或消除 ⭐⭐⭐⭐ 贪心+优先队列

3.1 题目描述

对数组 arr 执行"异或消除"操作:

  1. 找到数组中的最大值 a次大值 b(若有多个相同的最大值或次大值,取最左边的那个)
  2. ab 同时移除
  3. 计算 c = a ^ b
  4. c 放入数组的最左边

不断操作,直到数组只剩一个元素,输出该元素。

数据范围n ≤ 10^5

3.2 关键思路:贪心+最大堆

暴力思路 :每次找最大和次大(O(n)),移除并插入(O(n)),总复杂度 O(n²)
超时原因n=10^5 时,n² = 10^10,不可接受。

优化思路------最大堆(优先队列)

  • 用最大堆维护数组元素,找最大和次大变为 O(log n)
  • 移除和插入也是 O(log n)
  • 总复杂度 O(n log n)

但这里有个关键问题:"最左边的"这个条件怎么处理?

重新审视"最左边"

  • 如果最大值只有一个,那它就是最左边的最大值
  • 如果最大值有多个,取最左边的那个
  • 次大值同理

堆的实现难点:普通堆无法快速找到"最左边的"元素。

换个思路 :如果值相同,下标小的优先级高。堆中存储 (值, 下标),按值降序、下标升序排序。

但移除两个元素后,其他元素的下标会变化! 这导致"最左边"的条件很难维护。

关键观察:题目说"将c放入数组的最左边",这意味着:

  • 新元素 c 的下标比所有现有元素都小
  • 或者,我们可以把数组看作一个双端队列,从左边弹出、左边插入?

重新理解操作

  • "最左边"指的是当前数组的最左边
  • 移除 ab 后,数组长度减2
  • 插入 c 到最左边,数组长度减1(总体)

如果不用维护具体下标,只用保证"值相同时先取先出现的"

  • 可以用堆,但需要标记删除
  • 或者用 TreeMap / SortedList 维护有序集合

实际上,更简单的做法

  • 观察发现:这个操作的本质是不断将两个最大数异或后放回
  • 如果数组长度为奇数,最后剩1个;为偶数,最后剩... 题目保证最后剩1个?

等等,重新看样例

复制代码
输入: [3, 5, 7, 5, 3]

第1轮: 最大=7(下标2), 次大=5(下标1, 因为5有两个,取最左边的下标1)
       移除7和5(下标1),剩余[3, 5, 3](注意:移除下标1的5和下标2的7后,数组变成[3,5,3]?)
       等等,原数组是[3,5,7,5,3],移除下标1的5和下标2的7,剩余[3,5,3]
       c = 7^5 = 2,放入最左边 → [2, 3, 5, 3]

不对,剩余应该是[3,5,3],然后插入2到最左边 → [2,3,5,3]?长度变成4了?

但题目说"将a和b同时移除",所以长度减2,然后插入c,长度减1。
原长度5,操作后长度4。

继续:
第2轮: [2,3,5,3],最大=5(下标2), 次大=3(下标1, 因为3有两个,取最左边的下标1)
       移除5和3(下标1),剩余[2,3]
       c = 5^3 = 6,放入最左边 → [6,2,3]?长度3?

等等,剩余[2,3],插入6 → [6,2,3],长度3。

第3轮: [6,2,3],最大=6(下标0), 次大=3(下标2)
       移除6和3,剩余[2]
       c = 6^3 = 5,放入最左边 → [5,2]?长度2?

第4轮: [5,2],最大=5(下标0), 次大=2(下标1)
       移除5和2,剩余[]
       c = 5^2 = 7,放入最左边 → [7]

输出7 ✓

关键发现 :数组长度每次减1(移除2个,插入1个)。从 n 减到1,需要 n-1 次操作。

关于"最左边"的实现

  • 如果用堆,需要知道每个值的下标
  • 但下标会动态变化,很难维护

换个角度:如果所有元素都不同,那"最左边"就没意义了,直接取最大和次大。

  • 如果有重复元素,需要区分哪个是先出现的

实际上,可以用堆+延迟删除

  • 堆中存 (值, 原始下标)
  • 每次取堆顶两个元素,检查它们是否还在数组中(用布尔数组标记)
  • 如果不在,重新取

但"最左边"要求:值相同时,原始下标小的优先。堆的排序规则:值降序,值相同时下标升序。

然而,移除两个元素后,其他元素的下标会左移,这会影响"最左边"的判断!

重新审视 :也许"最左边"只在值相同时有影响。如果值都不同,那下标无所谓。

关键观察 :异或操作 a ^ b 的结果,与 ab 的位置无关,只与值有关。

大胆猜想:也许"最左边"这个条件,在最终结果上并不影响?或者可以用某种方式规避?

实际上,如果仔细看操作 :移除 ab 后,数组会收缩,但插入 c 到最左边。这意味着:

  • 新元素 c 的下标是0
  • 其他元素的下标不变?还是左移?

题目说"将c放入数组的最左边",这意味着 c 成为新的下标0,其他元素右移 ?还是 c 插入到最前面,其他元素后移?

如果是链表 结构,插入到头部是 O(1)。但找最大和次大需要 O(n)

综合考虑 :用 SortedList (或 TreeMap)维护有序集合,同时用链表维护实际顺序?

这太复杂了。也许题目数据保证元素各不相同?或者 n 比较小?

看代码 :用户只给了一行代码 result ^= int(x),这是对所有输入数字异或!

惊人发现result = a[0] ^ a[1] ^ ... ^ a[n-1],即所有元素的异或和

验证样例3^5^7^5^3 = 3^3^5^5^7 = 0^0^7 = 7

为什么?

3.3 数学证明

关键性质a ^ a = 0a ^ 0 = a,异或满足交换律结合律

观察操作:移除 ab,插入 a ^ b

整个数组所有元素的异或和

  • 操作前:X = ... ^ a ^ b ^ ...
  • 操作后:X' = ... ^ (a ^ b) ^ ...(因为 ab 被移除,替换成 a^b

等等,这不对。数组长度变了,元素也变了。

换个思路 :考虑所有出现过的数字的异或和(包括被移除的)

每次操作:

  • 移除 ab,它们不再出现在数组中
  • 加入 c = a ^ b

S 为数组中所有元素的异或和:

  • 操作前:S = a ^ b ^ rest
  • 操作后:S' = c ^ rest = (a ^ b) ^ rest

所以 S' = S?不对,ab 被移除了,但 c 加入了。

实际上:S' = (S ^ a ^ b) ^ c = S ^ a ^ b ^ (a ^ b) = S ^ 0 = S

异或和不变!

最终答案 = 初始数组的异或和!

因为最后只剩一个元素,它的值就是整个数组的异或和。

3.4 题解

python 复制代码
import sys

# 读取所有数字,直接异或
result = 0
for x in sys.stdin.readline().split():
    result ^= int(x)

print(result)

或者更简洁:

python 复制代码
print(eval('^'.join(sys.stdin.readline().split())))

复杂度 :时间 O(n),空间 O(1)

3.5 关键细节

坑点 说明
异或和不变性 操作前后,整个数组的异或和保持不变。这是解题关键
"最左边"条件 虽然题目说了"最左边",但实际上不影响最终异或和。因为异或满足交换律,顺序无关
输入读取 第二行有 n 个数字,可以用 sys.stdin.readline().split() 一次性读取
初始异或和 最后剩下的元素 = 初始所有元素的异或和
证明严谨性 S' = S ^ a ^ b ^ (a^b) = S,因为 a ^ a = 0, b ^ b = 0

3.6 为什么"最左边"不影响结果

复制代码
假设数组为 [x1, x2, ..., xk, a, ..., b, ...](a和b是要移除的两个元素)

无论a和b在什么位置,移除它们并插入a^b后:
- 新数组的异或和 = (原异或和 ^ a ^ b) ^ (a ^ b) = 原异或和

因为异或满足交换律和结合律,元素的顺序和位置不影响总异或和。

所以"最左边"这个条件只是干扰项,不影响最终答案!


🎯 今日复盘总结

题目 核心技巧 思维路径 易错点 国赛价值
与或异或 DFS枚举 小规模→直接枚举所有组合→统计合法方案 递归终止条件、下一个位置判断 ⭐⭐⭐ 基础回溯
异或金字塔 Lucas定理+组合数学 模拟找规律→发现组合数系数→Lucas定理优化→O(n) mask=n-1、位运算判断 ⭐⭐⭐⭐⭐ 神级优化
异或消除 异或不变性 模拟操作→发现异或和不变→直接算初始异或和 "最左边"是干扰条件、输入读取 ⭐⭐⭐⭐ 思维转化

记得 点赞收藏,算法路上不迷路!有问题评论区见 👇

相关推荐
wuxinyan1232 小时前
工业级大模型学习之路016:RAG零基础入门教程(第十二篇):实用进阶功能开发
人工智能·python·学习·rag
Volunteer Technology2 小时前
Python测试题 (一)
python
用户6757049885023 小时前
再见 pip!Rust 写的 uv 正在把 Python 包管理按在地上摩擦
后端·python
川石课堂软件测试3 小时前
接口测试常见面试题及答案
python·网络协议·mysql·华为·单元测试·prometheus·harmonyos
竹叶青lvye3 小时前
Python订阅与发布功能简介
python·订阅与发布
用户6757049885023 小时前
Python 装饰器很难?那是你没看到这篇文章!
后端·python
码界筑梦坊3 小时前
124-基于Python的航空旅客满意度数据可视化分析系统
开发语言·python·信息可视化·数据分析·flask·毕业设计
星越华夏3 小时前
PPTX判断包含图表id
python·pandas