大厂真题 / 阿里巴巴
阿里 3.28 笔试真题 - 后端开发岗
本场考试概述
考试时间:2026年3月28日 考试岗位:后端开发岗 难度评级:中等偏难
考点分析:
- 第一题:并查集(难度中等)
- 第二题:数论 + 素数计数(难度困难)
- 第三题:模拟 + 位运算(难度中等)
建议策略:
- 第一题是经典并查集,熟悉模板可以快速拿下
- 第二题数论推导有一定难度,但想清楚”隐式素数=素数幂”这一关键结论就能写出来
- 第三题模拟题,注意用数组/deque维护二进制位,避免大整数运算
第一题:列车相对静止
题目描述
给定 n 辆列车的相对静止矩阵信息,其中至少存在一辆列车真正静止。相对静止关系具有传递性。请计算可能真正静止的列车数量的最小值和最大值。
样例
输入
2
1 0
0 1
输出
1 1
题解:并查集
相对静止关系是等价关系,将列车划分为若干等价类。”真正静止”的列车必须恰好是某一个等价类的全部成员。最小值 = 最小等价类大小,最大值 = 最大等价类大小。
时间复杂度:O(n^2)。
import sys
input = sys.stdin.readline
def main():
n = int(input())
fa = list(range(n))
def find(x):
while fa[x] != x:
fa[x] = fa[fa[x]]
x = fa[x]
return x
def unite(x, y):
fa[find(x)] = find(y)
for i in range(n):
row = list(map(int, input().split()))
for j in range(n):
if row[j] == 1:
unite(i, j)
cnt = {}
for i in range(n):
r = find(i)
cnt[r] = cnt.get(r, 0) + 1
print(min(cnt.values()), max(cnt.values()))
main()
第二题:隐式素数
题目描述
正整数 n 的正因子个数为素数时,称 n 为隐式素数。给定闭区间 [l, r](最大 2*10^9),计算区间内隐式素数的个数。
样例
输入
2
1 10
10 20
输出
6
5
样例解释:[1,10] 中的隐式素数为 2, 3, 4, 5, 7, 9 共 6 个。
题解:数论 + 素数计数
关键推导:因子个数 d(n) = (e1+1)(e2+1)… 要是素数,n 只能有一个质因子,即 n = p^(q-1),其中 p、q 都是素数。
结论:隐式素数 = 所有素数(p^1, d=2)+ 所有素数的平方(p^2, d=3)+ 素数的四次方(p^4, d=5)+ …
实现:
- 用 Lucy DP 高效计算区间内素数个数,复杂度 O(n^{2/3})
- 预枚举所有高次素数幂,二分查找统计区间内个数
时间复杂度:O(n^{2/3})。
import sys
from bisect import bisect_left, bisect_right
from math import isqrt
input = sys.stdin.readline
def prime_count(n):
if n < 2:
return 0
s = isqrt(n)
while (s + 1) * (s + 1) <= n:
s += 1
while s * s > n:
s -= 1
small = [0] * (s + 2)
for i in range(2, s + 2):
small[i] = i - 1
large = [0] * (s + 2)
for k in range(1, s + 1):
large[k] = n // k - 1
for p in range(2, s + 1):
if small[p] <= small[p - 1]:
continue
cnt = small[p - 1]
p2 = p * p
upper = min(s, n // p2)
for k in range(1, upper + 1):
j = k * p
if j <= s:
large[k] -= large[j] - cnt
else:
large[k] -= small[n // j] - cnt
for v in range(s, p2 - 1, -1):
small[v] -= small[v // p] - cnt
return large[1]
def main():
MAX_VAL = 2_000_000_000
LIM = 44722
is_prime = [True] * (LIM + 1)
is_prime[0] = is_prime[1] = False
for i in range(2, isqrt(LIM) + 1):
if is_prime[i]:
for j in range(i * i, LIM + 1, i):
is_prime[j] = False
exps = [2, 4, 6, 10, 12, 16, 18, 22, 28, 30]
powers = []
for e in exps:
for p in range(2, LIM + 1):
if not is_prime[p]:
continue
val = p ** e
if val > MAX_VAL:
break
powers.append(val)
powers.sort()
T = int(input())
out = []
for _ in range(T):
l, r = map(int, input().split())
count = prime_count(r) - prime_count(l - 1)
lo = bisect_left(powers, l)
hi = bisect_right(powers, r)
count += hi - lo
out.append(str(count))
sys.stdout.write('\n'.join(out) + '\n')
main()
第三题:二进制操作
题目描述
给定初始值为 0 的整数 x 和操作字符串(+ - * /),每次操作后输出 x 的二进制中 1 的个数。n 最大 10^6,连续 * 操作会使数指数增长,无法用普通整型存储。
样例
输入
7
++-*-//
输出
1
1
1
1
1
0
0
题解:数组模拟二进制位
用数组存储 x 的每一位(LSB 在前),维护 popcount 计数器:
*:左移一位,popcount 不变/:右移一位,若移除位是 1 则 popcount 减 1+:进位链——从最低位找第一个 0,途中 1 变 0,该 0 变 1-:借位链——从最低位找第一个 1,途中 0 变 1,该 1 变 0
根据二进制计数器均摊分析,n 次操作总位翻转次数为 O(n)。
时间复杂度:O(n)(均摊)。
import sys
input = sys.stdin.readline
def main():
n = int(input())
s = input().strip()
max_bits = 2 * n + 10
bits = [0] * max_bits
lo = n + 5
hi = lo
popcount = 0
out = []
for ch in s:
if ch == '*':
if hi > lo:
lo -= 1
bits[lo] = 0
elif ch == '/':
if hi > lo:
if bits[lo] == 1:
popcount -= 1
bits[lo] = 0
lo += 1
while hi > lo and bits[hi - 1] == 0:
hi -= 1
elif ch == '+':
i = lo
while i < hi and bits[i] == 1:
bits[i] = 0
popcount -= 1
i += 1
if i < hi:
bits[i] = 1
else:
bits[hi] = 1
hi += 1
popcount += 1
else: # '-'
if popcount > 0:
i = lo
while i < hi and bits[i] == 0:
bits[i] = 1
popcount += 1
i += 1
if i < hi:
bits[i] = 0
popcount -= 1
while hi > lo and bits[hi - 1] == 0:
hi -= 1
out.append(str(popcount))
sys.stdout.write('\n'.join(out) + '\n')
main()
小结
- 第一题并查集模板题,将等价关系转化为等价类划分
- 第二题数论推导是核心:隐式素数 = 素数幂,结合 Lucy DP 处理大范围素数计数
- 第三题用数组模拟二进制位避免大整数,均摊分析保证线性复杂度