淘天笔试 淘天笔试题 0830

笔试时间:2025年8月30日

往年笔试合集:

2023春招秋招笔试合集

2024春招秋招笔试合集

第一题

给定一个长度为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 春招笔试合集 文章被收录于专栏

2025打怪升级记录,大厂笔试合集 C++, Java, Python等多种语言做法集合指南

全部评论

相关推荐

点赞 评论 收藏
分享
大名鼎鼎楚雨荨:我寻思这不才刚二面?
秋招的第一个offer,...
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务