大厂真题 / 网易
网易雷火 4.26 笔试真题
本场考试概述
考试时间:2026年4月26日 考试岗位:通用 难度评级:中等
考点分析:
- 第一题:数学公式 + 乘法逆元(难度中等)
- 第二题:FIFO 缓存模拟(难度中等)
- 第三题:三进制状压 DP(难度困难)
- 第四题:四层结构模拟(难度中等)
建议策略:
- 第一题和第二题是稳拿分的数学/模拟题,优先保证正确
- 第三题是区分度题,关键在于识别出传闻数 $w \leq 10$ 可以状压
- 第四题代码量大但逻辑清晰,按层建模逐条模拟即可
第一题:喵居
题目描述
供台上有 $n$ 团「猫灵元魂」,第 $i$ 团灵力值为 $a_i$。每次融合操作选择两团元魂(灵力值分别为 $x, y$),消耗 $x \times y$,融合后生成灵力值为 $x + y$ 的新元魂。将所有元魂融合为 1 个,求最小总消耗对 $10^9 + 7$ 取模的结果。
多组测试数据,$n$ 之和不超过 $10^5$。
样例
输入
2
3
1 2 3
4
2 2 2 2
输出
11
24
思路分析
第一步:发现总消耗与顺序无关
每次融合 $x, y$ 花费 $x \times y$,全局”元素平方和”增加了 $2xy$(因为 $(x+y)^2 = x^2 + y^2 + 2xy$)。初始平方和为 $\sum a_i^2$,最终只剩一个值为 $S = \sum a_i$ 的元素,平方和变为 $S^2$。
总花费恰好是平方和的增量除以 2:
\[\text{cost} = \frac{S^2 - \sum a_i^2}{2}\]与融合顺序完全无关。
第二步:取模计算
在模 $10^9 + 7$ 下,除以 2 等价于乘以 2 的模逆元。$10^9 + 7$ 是奇数,所以 $2^{-1} \equiv \frac{10^9 + 8}{2} = 500000004 \pmod{10^9 + 7}$。
题解代码
import sys
input = sys.stdin.readline
MOD = 10 ** 9 + 7
INV2 = (MOD + 1) // 2
def solve(a):
s = 0
sq = 0
for x in a:
xm = x % MOD
s = (s + xm) % MOD
sq = (sq + xm * xm) % MOD
return (s * s % MOD - sq + MOD) % MOD * INV2 % MOD
T = int(input())
results = []
for _ in range(T):
n = int(input())
a = list(map(int, input().split()))
results.append(str(solve(a)))
print('\n'.join(results))
复杂度分析
时间复杂度:$O(n)$,每个测试点遍历一次数组。 空间复杂度:$O(n)$,存储数组。
第二题:界面缓存
题目描述
MMORPG 游戏中的界面分为可缓存界面(关闭后暂存到缓存区,可快速恢复)和不可缓存界面(关闭后直接卸载)。缓存区容量为 $k$,采用 FIFO 淘汰策略。
给定 $n$ 种界面的类型和 $m$ 次操作(open x / close x),输出操作完毕后打开的界面列表和缓存区中的界面列表。
打开界面:若在缓存区中则恢复;若已打开则移到末尾;否则新建。 关闭界面:可缓存的进缓存区(满则 FIFO 淘汰);不可缓存的直接卸载。
样例
输入
4 9 2
1 1 0 1
open 1
open 2
open 3
close 2
close 3
close 1
open 4
open 1
open 4
输出
1 4
2
思路分析
第一步:理清两个队列
系统维护两个有序队列:打开列表记录当前正在显示的界面,缓存区暂存最近被关闭的可缓存界面。所有操作都是对这两个队列做增删移动。
第二步:分情况处理
open x:(1) 已打开 → 删除再追加到末尾;(2) 在缓存区 → 从缓存移出放入打开列表;(3) 都不在 → 新建并打开close x:不在打开列表则忽略。可缓存的放入缓存区(FIFO 淘汰队头),不可缓存的直接丢弃
第三步:数据结构选择
OrderedDict 同时支持 $O(1)$ 的按键查找、删除和有序遍历,非常适合维护这两个队列。
题解代码
import sys
from collections import OrderedDict
input = sys.stdin.readline
n, m, k = map(int, input().split())
t = list(map(int, input().split()))
cacheable = [False] + [ti == 1 for ti in t]
opened = OrderedDict()
cache = OrderedDict()
for _ in range(m):
parts = input().split()
op, x = parts[0], int(parts[1])
if op == "open":
if x in opened:
del opened[x]
opened[x] = True
elif x in cache:
del cache[x]
opened[x] = True
else:
opened[x] = True
else:
if x not in opened:
continue
del opened[x]
if cacheable[x]:
if len(cache) >= k:
cache.popitem(last=False)
cache[x] = True
out = []
out.append(' '.join(str(x) for x in opened) if opened else 'EMPTY')
out.append(' '.join(str(x) for x in cache) if cache else 'EMPTY')
print('\n'.join(out))
复杂度分析
时间复杂度:$O(m)$,每次操作只做常数次查找、删除、插入。 空间复杂度:$O(n)$,最多同时存储 $n$ 个界面。
第三题:传闻以此得解
题目描述
$n$ 个胜地可选,游历第 $i$ 个胜地消耗 $a_i$ 交子,每个胜地会给若干传闻各提供 1 条线索。一个传闻攒够 $\geq 2$ 条线索才算解开。每个胜地最多游历一次,总花费不超过预算 $m$。求最多能解开多少个传闻。
$n \leq 100$,$m \leq 10^9$,传闻数 $w \leq 10$。
样例
输入
3
4 5 3
2 2
1 2
3 2
1 3
2 1
2
4 2
2 3
3 3 2
2 1
1
2 1
2
3 2
1 2 2 3
2 2
1 2
3
输出
1
0
0
思路分析
第一步:为什么不能直接背包?
经典背包把”花了多少钱”当状态,但预算 $m$ 可达 $10^9$,开不了这么大的数组。
第二步:换状态维度——三进制状压
传闻总共才 $w \leq 10$ 个。每个传闻的线索数只关心三种情况:0 条、1 条、$\geq 2$ 条。把所有传闻的线索情况打包成一个三进制数,总状态数 $3^{10} = 59049$,完全可以枚举。
第三步:DP 定义与转移
定义 $f[s]$ = 让所有传闻的线索情况变成状态 $s$ 所需的最小花费。初始 $f[0] = 0$,其余为 $+\infty$。
对每个胜地(0/1 背包,逆序枚举),如果选了它,就把对应传闻位的三进制数字各加 1(已是 2 则不加)。最终遍历所有 $f[s] \leq m$ 的状态,数有几位等于 2,取最大值。
题解代码
import sys
input = sys.stdin.readline
def solve():
n, m, w = map(int, input().split())
sites = []
for _ in range(n):
parts = list(map(int, input().split()))
a, k = parts[0], parts[1]
clues = list(map(int, input().split()))
mask = 0
for c in clues:
mask |= 1 << (c - 1)
sites.append((a, mask))
pw3 = [1] * (w + 1)
for i in range(1, w + 1):
pw3[i] = pw3[i - 1] * 3
total = pw3[w]
INF = float('inf')
f = [INF] * total
f[0] = 0
for cost, mask in sites:
for s in range(total - 1, -1, -1):
if f[s] < INF and f[s] + cost <= m:
ns = s
tmp = mask
while tmp:
j = (tmp & -tmp).bit_length() - 1
digit = (ns // pw3[j]) % 3
if digit < 2:
ns += pw3[j]
tmp &= tmp - 1
val = f[s] + cost
if val < f[ns]:
f[ns] = val
best = 0
for s in range(total):
if f[s] <= m:
cnt = 0
tmp = s
for _ in range(w):
if tmp % 3 == 2:
cnt += 1
tmp //= 3
if cnt > best:
best = cnt
return best
T = int(input())
results = []
for _ in range(T):
results.append(str(solve()))
print('\n'.join(results))
复杂度分析
时间复杂度:$O(n \times 3^w \times w)$,每个胜地遍历所有状态并更新三进制位。 空间复杂度:$O(3^w)$,DP 数组。
第四题:红点系统
题目描述
实现一个四层红点系统:
- 红点类型(最底层):有”显示/隐藏”两种状态
- 红点类型集合:包含若干红点类型,任意一个显示则集合显示
- 按钮:绑定若干红点类型/集合,任意一个显示则按钮亮
- 界面:绑定若干按钮,任意一个亮则界面显示红点
支持 6 种操作:Show/Hide 修改红点状态,Ask/AskUI 查询按钮/界面状态,Remove/Bind 修改按钮绑定。状态改变后,若界面从”亮”变”灭”则输出界面名。
样例
输入
3 1 3 2
Red1 1
Red2 0
Red3 1
RedGP1 2 Red1 Red2
Btn1 1 Red1
Btn2 1 RedGP1 Red3
Btn3 2 RedGP1 Red3
Panel1 1 Btn1
Panel2 2 Btn2 Btn3
6
Ask Btn2
Hide Red3
Hide Red1
Ask Btn3
AskUI Panel1
Show Red2
输出
YES
Panel1
Panel2
NO
NO
思路分析
第一步:理清四层结构
每一层的判定逻辑相同:任意一个子项亮,父项就亮。从底向上逐层查询即可。
第二步:分操作处理
Show/Hide:修改红点类型状态,改后检查所有界面是否从亮变灭Ask/AskUI:纯查询,沿四层自底向上判定Remove/Bind:修改按钮绑定关系,改后同样检查界面变化
第三步:界面状态变化检测
每次执行修改操作前,记录所有界面当前状态。操作后逐个比对,从亮变灭的按输入顺序输出。
数据规模小($n, m, x, y$ 均不大),暴力逐层查询完全够用。
题解代码
import sys
input = sys.stdin.readline
N, M, X, Y = map(int, input().split())
red_state = {}
for _ in range(N):
parts = input().split()
red_state[parts[0]] = parts[1] == '1'
group_members = {}
for _ in range(M):
parts = input().split()
name, num = parts[0], int(parts[1])
group_members[name] = parts[2:2 + num]
btn_order = []
btn_binds = {}
for _ in range(X):
parts = input().split()
name, num = parts[0], int(parts[1])
btn_order.append(name)
btn_binds[name] = set(parts[2:2 + num])
panel_order = []
panel_btns = {}
for _ in range(Y):
parts = input().split()
name, num = parts[0], int(parts[1])
panel_order.append(name)
panel_btns[name] = parts[2:2 + num]
def is_active(name):
if name in red_state:
return red_state[name]
if name in group_members:
return any(red_state.get(r, False) for r in group_members[name])
return False
def btn_active(btn):
return any(is_active(b) for b in btn_binds.get(btn, set()))
def panel_active(panel):
return any(btn_active(b) for b in panel_btns.get(panel, []))
S = int(input())
results = []
for _ in range(S):
parts = input().split()
op = parts[0]
if op == "Ask":
results.append("YES" if btn_active(parts[1]) else "NO")
elif op == "AskUI":
results.append("YES" if panel_active(parts[1]) else "NO")
else:
before = {p: panel_active(p) for p in panel_order}
if op == "Show":
if parts[1] in red_state:
red_state[parts[1]] = True
elif op == "Hide":
if parts[1] in red_state:
red_state[parts[1]] = False
elif op == "Remove":
btn, target = parts[1], parts[2]
if btn in btn_binds:
btn_binds[btn].discard(target)
elif op == "Bind":
btn, target = parts[1], parts[2]
if btn in btn_binds and target not in btn_binds[btn]:
btn_binds[btn].add(target)
for p in panel_order:
if before[p] and not panel_active(p):
results.append(p)
print('\n'.join(results))
复杂度分析
时间复杂度:$O(S \times Y \times X \times M)$,每次操作最坏遍历所有界面、按钮、绑定项判断状态。规模很小,暴力够用。 空间复杂度:$O(N + M + X + Y)$,哈希表存储各实体及绑定关系。
小结
- 第一题的核心发现是总消耗与融合顺序无关——利用平方和的变化推导出闭式公式 $\frac{S^2 - \sum a_i^2}{2}$,取模时用乘法逆元处理除法
- 第二题是纯模拟题,
OrderedDict同时满足有序遍历和 $O(1)$ 查找删除,是维护 FIFO 队列的理想选择 - 第三题的关键洞察是传闻数 $w \leq 10$,每个传闻只需区分 0/1/$\geq$2 三种状态,三进制状压后状态空间仅 $3^{10} \approx 6 \times 10^4$
- 第四题无算法难度,考验工程能力——理清四层结构的”任一子项亮则父项亮”逻辑,逐操作模拟即可