淘天笔试 淘天笔试题 0830
笔试时间:2025年8月30日
往年笔试合集:
第一题
给定一个长度为n的非负整数序列{a₁,a₂,…,aₙ}以及对应的非负整数权重序列{w₁,w₂,…,wₙ}。
其中,第i篇论文被引用aᵢ次,具有权重wᵢ。
定义“加权H指数”为最大的非负整数x,使得在所有被引用次数至少x的论文中,它们的权重之和至少为x。
请你计算并输出该加权H指数。
输入描述
每个测试文件均包含多组测试数据。第一行输入一个整数T(1 ≤ T ≤ 10⁴)代表数据组数,每组测试数据描述如下:
第一行输入一个整数n(1 ≤ n ≤ 2×10⁵),表示论文数量;
第二行输入n个整数a₁,a₂,…,aₙ(0 ≤ aᵢ ≤ 10⁹),其中aᵢ表示第i篇论文的引用次数;
第三行输入n个整数w₁,w₂,…,wₙ(0 ≤ wᵢ ≤ 10⁹),其中wᵢ表示第i篇论文的权重。
除此之外,保证单个测试文件的n和不超过2×10⁵。
输出描述
每组测试数据输出一行一个整数,表示加权H指数。
样例输入
1
5
53142
12132
样例输出
4
为了解决这个问题,我们需要计算给定论文集合的加权H指数。加权H指数定义为最大的非负整数x,使得所有被引用次数至少为x的论文的权重之和至少为x。
参考题解
问题分析:我们需要找到最大的x,使得满足条件的论文的权重之和至少为x。关键在于如何高效地计算这个x,而不是暴力枚举所有可能的x值。关键观察:x的取值范围受限于最大引用次数和总权重之和。x必须同时不超过最大引用次数和总权重之和。算法选择:排序:将论文按引用次数升序排序,以便快速筛选满足条件的论文。后缀和数组:预处理权重之和的后缀数组,以便快速计算从任意位置到末尾的权重和。二分查找:在可能的x范围内进行二分查找,检查每个中间值是否满足条件,从而高效地确定最大的x。
C++:
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T;
cin >> T;
while (T--) {
int n;
cin >> n;
if (n == 0) {
cout << 0 << '\n';
continue;
}
vector<int> a_list(n), w_list(n);
for (int i = 0; i < n; ++i) {
cin >> a_list[i];
}
for (int i = 0; i < n; ++i) {
cin >> w_list[i];
}
// 组合并按a值排序
vector<pair<int, int>> papers(n);
for (int i = 0; i < n; ++i) {
papers[i] = {a_list[i], w_list[i]};
}
sort(papers.begin(), papers.end());
// 提取排序后的a和w
vector<int> a_sorted(n), w_sorted(n);
for (int i = 0; i < n; ++i) {
a_sorted[i] = papers[i].first;
w_sorted[i] = papers[i].second;
}
// 计算后缀和
vector<long long> suffix_sum(n + 1, 0);
for (int i = n - 1; i >= 0; --i) {
suffix_sum[i] = suffix_sum[i + 1] + w_sorted[i];
}
// 确定二分查找边界
int max_a = a_sorted.back();
long long total_weight = suffix_sum[0];
int high_bound = min(max_a, (int)total_weight) + 1;
// 二分查找
int low = 0, high = high_bound;
while (low < high) {
int mid = (low + high) / 2;
// 找到第一个大于等于mid的位置
auto it = lower_bound(a_sorted.begin(), a_sorted.end(), mid);
int idx = it - a_sorted.begin();
long long total_w = suffix_sum[idx];
if (total_w >= mid) {
low = mid + 1;
} else {
high = mid;
}
}
cout << low - 1 << '\n';
}
return 0;
}
Java:
import java.util.*;
import java.io.*;
public class MaxValidValue {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int T = Integer.parseInt(br.readLine());
while (T-- > 0) {
int n = Integer.parseInt(br.readLine());
if (n == 0) {
System.out.println(0);
continue;
}
int[] aList = new int[n];
int[] wList = new int[n];
StringTokenizer st = new StringTokenizer(br.readLine());
for (int i = 0; i < n; i++) {
aList[i] = Integer.parseInt(st.nextToken());
}
st = new StringTokenizer(br.readLine());
for (int i = 0; i < n; i++) {
wList[i] = Integer.parseInt(st.nextToken());
}
// 组合并按a值排序
int[][] papers = new int[n][2];
for (int i = 0; i < n; i++) {
papers[i][0] = aList[i];
papers[i][1] = wList[i];
}
// 按a值升序排序
Arrays.sort(papers, Comparator.comparingInt(p -> p[0]));
// 提取排序后的a和w
int[] aSorted = new int[n];
int[] wSorted = new int[n];
for (int i = 0; i < n; i++) {
aSorted[i] = papers[i][0];
wSorted[i] = papers[i][1];
}
// 计算后缀和
long[] suffixSum = new long[n + 1];
for (int i = n - 1; i >= 0; i--) {
suffixSum[i] = suffixSum[i + 1] + wSorted[i];
}
// 确定二分查找边界
int maxA = aSorted[n - 1];
long totalWeight = suffixSum[0];
int highBound = Math.min(maxA, (int)totalWeight) + 1;
// 二分查找
int low = 0, high = highBound;
while (low < high) {
int mid = (low + high) / 2;
// 找到第一个大于等于mid的位置(模拟bisect_left)
int idx = bisectLeft(aSorted, mid);
long totalW = suffixSum[idx];
if (totalW >= mid) {
low = mid + 1;
} else {
high = mid;
}
}
System.out.println(low - 1);
}
}
// 实现bisect_left功能:找到第一个大于等于target的元素索引
private static int bisectLeft(int[] arr, int target) {
int low = 0, high = arr.length;
while (low < high) {
int mid = (low + high) / 2;
if (arr[mid] < target) {
low = mid + 1;
} else {
high = mid;
}
}
return low;
}
}
Python:
import bisect T = int(input().strip()) for _ in range(T): n = int(input().strip()) a_list = list(map(int, input().split())) w_list = list(map(int, input().split())) if n == 0: print(0) continue papers = list(zip(a_list, w_list)) papers.sort(key=lambda x: x[0]) a_sorted = [p[0] for p in papers] w_sorted = [p[1] for p in papers] suffix_sum = [0] * (n + 1) for i in range(n - 1, -1, -1): suffix_sum[i] = suffix_sum[i + 1] + w_sorted[i] max_a = a_sorted[-1] if n > 0 else 0 total_weight = suffix_sum[0] high_bound = min(max_a, total_weight) + 1 low, high = 0, high_bound while low < high: mid = (low + high) // 2 idx = bisect.bisect_left(a_sorted, mid) total_w = suffix_sum[idx] if total_w >= mid: low = mid + 1 else: high = mid print(low - 1)
第二题
给定一个正整数 n,一个长度为 n 的排列 p 是由 {1,2,…,n} 这 n 个整数按任意顺序组成的数组,其中每个整数恰好出现一次;我们定义该排列的代价为满足 pᵢ × pᵢ₊₁ 是完全平方数的索引 i 的个数(1 ≤ i < n);请你构造一个排列 p,使其代价最大。
输入描述
每个测试文件均包含多组测试数据。第一行输入一个整数 T(1 ≤ T ≤ 10⁴),表示测试用例数; 此后 T 行,每行输入一个整数 n(2 ≤ n ≤ 2×10⁵),表示排列的长度; 此外,保证所有测试用例的 n 之和不超过 2×10⁵。
输出描述
对于每组测试数据,新起一行,输出一个长度为 n 的排列 p 为最大代价的排列。 如果存在多个解决方案,您可以输出任意一个,系统会自动判定是否正确。注意,自测运行功能可能因此返回错误结果,请自行检查答案正确性。
样例输入
2
5
10
样例输出
2 3 1 4 5
10 7 6 1 4 9 2 8 3 5
参考题解
预处理核计算:使用筛法预处理每个数的最小质因数,然后计算每个数的核。核是通过分解质因数后,保留指数为奇数的质因数的乘积得到的。分组数字:对于每个测试用例,将1到n的数字按核分组。构造排列:将各组按核的升序排列,组内数字也按升序排列,以确保相同核的数字连续放置,从而最大化相邻对乘积为完全平方数的数量。
C++:
#include <iostream>
#include <vector>
#include <map>
#include <cmath>
#include <sstream>
#include <algorithm>
using namespace std;
const int N_MAX = 200000;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
// 计算最小素因子(sieve of Eratosthenes变体)
vector<int> min_prime(N_MAX + 1);
for (int i = 0; i <= N_MAX; ++i) {
min_prime[i] = i;
}
int sqrt_n = sqrt(N_MAX);
for (int i = 2; i <= sqrt_n; ++i) {
if (min_prime[i] == i) { // i是素数
for (int j = i * i; j <= N_MAX; j += i) {
if (min_prime[j] == j) {
min_prime[j] = i;
}
}
}
}
// 计算core数组:core[i]是i的所有出现奇数次的质因子的乘积
vector<int> core(N_MAX + 1);
core[1] = 1; // 1的core值为1
for (int i = 2; i <= N_MAX; ++i) {
int current = i
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
2025打怪升级记录,大厂笔试合集 C++, Java, Python等多种语言做法集合指南

