美团笔试 美团笔试题 0830
笔试时间:2025年8月30日
往年笔试合集:
第一题:小美的神秘运算2
小美有一个数字 n,小美打算按照以下规则对 n 进行操作:
如果 n 是偶数,让 n 除以 2;
否则,让 n 乘以 3 再加 1。
小美想知道,在操作 k 次后,n 会变成多少。
输入描述
输入一行两个整数 n,k,满足条件:
2 ≤ n ≤ 10¹⁸,0 ≤ k ≤ 10¹⁸
输出描述
输出 n 操作 k 次后的结果。
样例输入
100 10
样例输出
22
参考题解
对任意(n)按规则迭代,一旦到达(1),后续进入长度为3的循环:1→4→2→1→…因此,当首次到达1后,剩余操作只取决于剩余步数对3取模。
C++:
#include <iostream> using namespace std; int main() { int n, k; // 读取输入n和k cin >> n >> k; // 核心变换循环:k>0且n≠1时执行 while (k > 0 && n != 1) { if (n % 2 == 0) { // 偶数:除以2(整数除法) n /= 2; } else { // 奇数:变为3n+1 n = 3 * n + 1; } k--; // 每次变换后k减1 } // 若仍有剩余k(n已为1),利用周期序列[1,4,2]计算 if (k > 0) { int cycle[] = {1, 4, 2}; // 1的后续循环序列,周期3 int r = k % 3; // 取余数确定索引 n = cycle[r]; } // 输出最终结果 cout << n << endl; return 0; }
Java:
import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); // 读取输入n和k int n = scanner.nextInt(); int k = scanner.nextInt(); // 核心变换循环:k>0且n≠1时执行 while (k > 0 && n != 1) { if (n % 2 == 0) { // 偶数:除以2(整数除法) n /= 2; } else { // 奇数:变为3n+1 n = 3 * n + 1; } k--; // 每次变换后k减1 } // 若仍有剩余k(n已为1),利用周期序列[1,4,2]计算 if (k > 0) { int[] cycle = {1, 4, 2}; // 1的后续循环序列,周期3 int r = k % 3; // 取余数确定索引 n = cycle[r]; } // 输出最终结果 System.out.println(n); scanner.close(); } }
Python:
n, k = map(int, input().split()) while k > 0 and n != 1: if n % 2 == 0: n //= 2 else: n = 3*n + 1 k -= 1 if k > 0: r = k % 3 n = [1, 4, 2][r] print(n)
第二题:小美的有限小数
小美是一位数学爱好者,她想知道给定的有理数p/q在k进制下是否为一个有限小数。换句话说,她希望判断在基数为k的表示中,该分数的小数部分是否会在有限位后结束,而不是无限循环。例如,m½在十进制下表示为0.5,是有限小数;相比之下,1/3在十进制下表示为0.3(循环),不是有限小数。
输入描述
第一行输入一个整数T (1 ≤ T ≤ 10⁵),表示测试数据的组数。
接下来T行,每行输入三个整数p,q,k (1 ≤ p,q ≤ 10¹⁸, 2 ≤ k ≤ 10¹⁸),分别表示分子p、分母q和进制基数k。
输出描述
对于每一组测试数据,输出一行结果。若p/q在k进制下是有限小数,则输出yes;否则输出no。
样例输入
3
1 2 10
1 3 10
3 4 2
样例输出
yes
no
yes
参考题解
C++:
#include <bits/stdc++.h> using namespace std; long long gcdll(long long x, long long y) { return std::gcd(x, y); } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int T; if (!(cin >> T)) return 0; while (T--) { long long a, b, c; cin >> a >> b >> c; long long g = gcdll(a, b); long long d = b / g; if (c == 1) { cout << (d == 1 ? "yes\n" : "no\n"); continue; } while (d > 1) { long long t = gcdll(c, d); if (t == 1) { cout << "no\n"; goto next_case; } while (d % t == 0) d /= t; } cout << "yes\n"; next_case: (void)0; } return 0; }
Java:
import java.io.*; import java.util.*; public class Main { static long gcd(long a, long b) { while (b != 0) { long t = a % b; a = b; b = t; } return Math.abs(a); } public static void main(String[] args) throws Exception { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); String line = br.readLine(); if (line == null) return; int T = Integer.parseInt(line.trim()); StringBuilder out = new StringBuilder(); for (int tc = 0; tc < T; tc++) { StringTokenizer st; while (true) { line = br.readLine(); if (line == null) return; // unexpected EOF line = line.trim(); if (!line.isEmpty()) break; } st = new StringTokenizer(line); long a = Long.parseLong(st.nextToken()); long b = Long.parseLong(st.nextToken()); long c = Long.parseLong(st.nextToken()); long g = gcd(a, b); long d = b / g; if (c == 1) { out.append(d == 1 ? "yes" : "no").append('\n'); continue; } boolean ok = true; while (d > 1) { long t = gcd(c, d); if (t == 1) { ok = false; break; } while (d % t == 0) d /= t; } out.append(ok ? "yes" : "no").append('\n'); } System.out.print(out.toString()); } }
Python:
import sys, math def work(): a, b, c = map(int, sys.stdin.readline().split()) g = math.gcd(a, b) d = b // g if1 == c: print("yes"if1 == d else"no") return while d > 1: t = math.gcd(c, d) if1 == t: print("no") return while d % t == 0: d //= t print("yes") def main(): n = int(sys.stdin.readline()) i = 0 while i < n: work() i += 1 if __name__ == "__main__": main()
第三题
给定一棵以节点1为根的有n个节点的树,每个节点i有一个正整数权值wᵢ,初始被涂成黑色或白色。你需要支持以下两种操作:
1. 翻转节点x的颜色(黑色变白色,白色变黑色);
2. 查询从根节点1到节点x的路径上所有黑色节点的权值的最小公倍数(若路径上无黑色节点,则记为1)。
名词解释最小公倍数:最小公倍数是能够被给定整数集合中每个整数整除的最小正整数,记为lcm
输入描述
第一行输入两个整数n和q,1 ≤ n,q ≤ 2×10⁵,分别表示树的节点数量和操作数量。
第二行输入n个整数w₁,w₂,…,wₙ,1 ≤ wᵢ ≤ 100,分别表示节点1到节点n的权值。
第三行输入一个长度为n、仅由字符B和W构成的字符串c,其中cᵢ=B表示节点i初始化为黑色,cᵢ=W表示初始化为白色。
接下来n−1行,每行输入两个整数uᵢ和vᵢ(1 ≤ uᵢ,vᵢ ≤ n;uᵢ≠vᵢ),表示一条连接节点uᵢ和节点vᵢ的无向边。保证这n个节点构成一棵根为1的树。
接下来q行,每行输入两个整数t和x(t ∈ {1,2}, 1 ≤ x ≤ n),表示一次操作:
当t=1时,执行翻转操作;
当t=2时,执行查询操作。
输出描述
对于每次查询操作,输出一行结果。
样例输入
5 5
2 3 4 5 6
BWBWB
1 2
1 3
3 4
3 5
2 4
1 3
2 4
1 1
2 5
样例输出
4
2
6
说明:
在这个样例中,初始颜色为:
节点1→B;
节点2→W;
节点3→B;
节点4→W;
节点5→B。
1. 查询2 4:路径1−3−4上黑色节点为1,3,权值分别为2,4,lcm(2,4)=4
2. 翻转节点3后再次查询2 4:路径上仅剩节点1为黑色,lcm(2)=2
3. 再翻转节点1后第三次查询2 5:路径1−3−5上仅有节点5为黑色,lcm(6)=6
参考题解
C++:
#include <bits/stdc++.h> using namespace std; static const int MOD = 1000000007; static const int MAXN = 200005; static const int MAXW = 101; static const int NUM_PRIMES = 25; int n, q; vector<int> adj[MAXN]; int w[MAXN]; bool is_black[MAXN]; // HLD arrays int parent_[MAXN], depth_[MAXN], sz_[MAXN], heavy_[MAXN]; int dfn_[MAXN], top_[MAXN], rid_[MAXN], timer_ = 0; // primes and factors up to 100 vector<int> primes; int prime_idx[MAXW]; // map prime -> index [0..NUM_PRIMES) vector<pair<int,int>> factors_[MAXW]; // factors for 1..100 // segment tree: each node stores an array<int,NUM_PRIMES> with max exponents vector<array<int,NUM_PRIMES>> seg; // size 4*n vector<array<int,NUM_PRIMES>> initial_; // size n+1 // ---------- utils ---------- long long modpow(long long a, long long e) { long long r = 1 % MOD; while (e) { if (e & 1) r = (r * a) % MOD; a = (a * a) % MOD; e >>= 1; } return r; } array<int,NUM_PRIMES> merge_arr(const array<int,NUM_PRIMES>& A, const array<int,NUM_PRIMES>& B) { array<int,NUM_PRIMES> C{}; for (int i = 0; i < NUM_PRIMES; ++i) C[i] = max(A[i], B[i]); return C; } array<int,NUM_PRIMES> zero_arr() { array<int,NUM_PRIMES> Z{}; for (int i = 0; i < NUM_PRIMES; ++i) Z[i] = 0; return Z; } array<int,NUM_PRIMES> get_exponents(int x) { array<int,NUM_PRIMES> res = zero_arr(); for (auto &pr : factors_[x]) { int p = pr.first, e = pr.second; int idx = prime_idx[p]; res[idx] = e; } return res; } // ---------- sieve & factor table ---------- void sieve_and_factors() { vector<bool> siv(MAXW, true); siv[0] = siv[1] = false; memset(prime_idx, -1, sizeof(prime_idx)); for (int p = 2; p < MAXW; ++p) { if (siv[p]) { prime_idx[p] = (int)primes.size(); primes.push_back(p); for (int i = p * p; i < MAXW; i += p) siv[i] = false; } } // factors for 1..100 for (int i = 1; i < MAXW; ++i) { int num = i; for (int p : primes) { if (1LL * p * p > num) break; if (num % p == 0) { int cnt = 0; while (num % p == 0) { num /= p; ++cnt; } factors_[i].push_back({p, cnt}); } } if (num > 1) factors_[i].push_back({num, 1}); } } // ---------- HLD ---------- int dfs1(int u, int p, int d) { parent_[u] = p; depth_[u] = d; sz_[u] = 1; heavy_[u] = 0; int maxsz = 0; for (int v : adj[u]) if (v != p) { dfs1(v, u, d + 1); sz_[u] += sz_[v]; if (sz_[v] > maxsz) { maxsz = sz_[v]; heavy_[u] = v; } } return sz_[u]; } void dfs2(int u, int t) { top_[u] = t; dfn_[u] = ++timer_; rid_[timer_] = u; if (heavy_[u]) dfs2(heavy_[u], t); for (int v : adj[u]) if (v != parent_[u] && v != heavy_[u]) { dfs2(v, v); } } // ---------- segment tree ---------- void seg_build(int p, int l, int r) { if ((int)seg.size() == 0) return; // guard if (l == r) { seg[p] = initial_[l]; return; } int m = (l + r) >> 1; seg_build(p<<1, l, m); seg_build(p<<1|1, m+1, r); seg[p] = merge_arr(seg[p<<1], seg[p<<1|1]); } void seg_update(int p, int l, int r, int pos, const array<int,NUM_PRIMES>& val) { if (l == r) { seg[p] = val; return; } int m = (l + r) >> 1; if (pos <= m) seg_update(p<<1, l, m, pos, val); else seg_update(p<<1|1, m+1, r, pos, val); seg[p] = merge_arr(seg[p<<1], seg[p<<1|1]); } array<int,NUM_PRIMES> seg_query(int p, int l, int r, int ql, int qr) { if (ql > qr) return zero_arr(); if (ql <= l && r <= qr) return seg[p]; int m = (l + r) >> 1; array<int,NUM_PRIMES> L = zero_arr(), R = zero_arr(); if (ql <= m) L = seg_query(p<<1, l, m, ql, qr); if (qr > m) R = seg_query(p<<1|1, m+1, r, ql, qr); return m
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
2025打怪升级记录,大厂笔试合集 C++, Java, Python等多种语言做法集合指南