当前位置: 首页 > web >正文

游游的数组询问

游游的数组询问

游游有一个长度为 n的数组,每个数的权值为它的质因子个数。现在游游想要删除一段长度刚好为 k 的子数组,删除后需要使剩下的数的权值和最大。问这个权值和是多少?

时间限制:C/C++ 1秒,其他语言2秒

空间限制:C/C++ 256M,其他语言512M

题目重述

游游有一个长度为n的数组,每个元素的权值是其质因子的个数。现在要删除一个长度恰好为k的子数组(连续的k个元素),使得剩下的元素的权值和最大。求这个最大的权值和。

示例分析

输入:​

5 2
6 2 4 1 3

输出:​

4

解释:​

  • 计算每个元素的权值(质因子个数):
    • 6:质因子2和3 → 权值2
    • 2:质因子2 → 权值1
    • 4:质因子2 → 权值1
    • 1:无质因子 → 权值0
    • 3:质因子3 → 权值1
  • 删除子数组[4,1](权值和为1+0=1),剩下的[6,2,3]权值和为2+1+1=4

解题思路

  1. 预处理质因子个数​:使用筛法预处理1到10000(因为a_i ≤ 10^4)的质因子个数,存储每个数的不同质因子个数。

  2. 计算权值数组​:根据预处理结果,计算每个元素的权值,形成权值数组w。

  3. 滑动窗口求最小权值和子数组​:

    • 问题转化为:在权值数组w中,找到一个长度为k的子数组,其权值和最小(因为删除权值和最小的子数组,剩下的权值和最大)。
    • 使用滑动窗口技术,计算所有长度为k的子数组的和,并记录最小值。
  4. 计算最终结果​:

    • 总权值和减去最小子数组和即为答案。

关键步骤详解

  1. 预处理质因子个数​:

    • 使用埃拉托斯特尼筛法(筛法)预处理每个数的最小质因子(Smallest Prime Factor, SPF)。
    • 对于每个数,分解质因子并统计不同质因子的个数。
  2. 计算权值数组​:

    • 对于数组中的每个元素,查询其质因子个数,得到权值数组w。
  3. 滑动窗口求最小子数组和​:

    • 初始化窗口和为前k个元素的和。
    • 滑动窗口,每次移动一个元素,更新窗口和,并记录最小值。
  4. 计算最终结果​:

    • 总权值和为权值数组w的所有元素之和。
    • 最大剩余权值和 = 总权值和 - 最小子数组和。

解决代码

import sysdef precompute_prime_factors(max_num):# 筛法预处理每个数的最小质因子spf = [0] * (max_num + 1) #创建一个长度为 max_num + 1 的数组,初始化为0spf[0], spf[1] = 0, 1 # 特殊处理0和1(0没有质因数,1的质因数定义为1)for i in range(2, max_num + 1): #从2开始遍历每个数字if spf[i] == 0: #如果 spf[i] 仍然是0,说明 i 是质数(因为还没有比它小的数标记过它)spf[i] = i #将质数 i 的最小质因数设为它自己# 对于每个质数 i,从 i*i 开始标记它的所有倍数for j in range(i * i, max_num + 1, i):#如果某个数 j 还没有被标记过最小质因数(spf[j] == 0),则 i 就是它的最小质因数if spf[j] == 0: spf[j] = i# 计算每个数的不同质因子个数factor_count = [0] * (max_num + 1)for num in range(2, max_num + 1):x = numfactors = set()while x != 1:factors.add(spf[x])x = x // spf[x]factor_count[num] = len(factors)return factor_countdef solve():input = sys.stdin.read().split()ptr = 0n, k = map(int, input[ptr:ptr + 2])ptr += 2a = list(map(int, input[ptr:ptr + n]))ptr += nif n == 0:print(0)returnmax_a = max(a)factor_count = precompute_prime_factors(max_a)w = []for num in a:if num == 1:w.append(0)else:w.append(factor_count[num])total = sum(w)if k == 0:print(total)return# 滑动窗口找长度为k的最小和子数组min_sum = float('inf')current_sum = 0left = 0for right in range(n):current_sum += w[right]if right >= k - 1:if current_sum < min_sum:min_sum = current_sumcurrent_sum -= w[left]left += 1print(total - min_sum)if __name__ == "__main__":solve()

复杂度分析

  • 预处理质因子个数​:O(M log log M),其中M是最大的a_i(这里是10^4)。
  • 计算权值数组​:O(n),遍历数组一次。
  • 滑动窗口求最小子数组和​:O(n),每个元素被处理两次(加入和移除窗口)。
  • 总复杂度​:O(M log log M + n),对于n ≤ 10^5和M=10^4是完全可行的。

示例验证

输入:​

5 2
6 2 4 1 3

处理过程:​

  1. 预处理1到6的质因子个数:
    • 1: 0
    • 2: 1
    • 3: 1
    • 4: 1
    • 5: 1
    • 6: 2
  2. 权值数组w:[2, 1, 1, 0, 1]
  3. 总权值和:2 + 1 + 1 + 0 + 1 = 5
  4. 滑动窗口找长度为2的最小和子数组:
    • [2,1]:和=3
    • [1,1]:和=2
    • [1,0]:和=1(最小值)
    • [0,1]:和=1
  5. 最大剩余权值和:5 - 1 = 4

输出:​

4

为什么从从 i*i 开始标记倍数?

在生成最小质因数(SPF)表时,从 i*i 开始标记倍数,而不是从 2*i 开始,这是为了​​避免重复标记​​,提高算法效率。具体原因如下:


1. ​​为什么不是从 2*i 开始?​

如果从 2*i 开始标记,例如 i = 5 时,你会标记:

  • 5 * 2 = 10(最小质因数是2,但 spf[10] 已经被 i=2 时标记为2)
  • 5 * 3 = 15(最小质因数是3,但 spf[15] 已经被 i=3 时标记为3)
  • 5 * 4 = 20(最小质因数是2,已经被 i=2 标记)
  • 5 * 5 = 25(这才是 i=5 需要处理的最小未标记数)

​问题​​:10, 15, 20 的最小质因数已经被更小的质数(2或3)标记过了,再让 i=5 去检查它们就是​​重复工作​​。


2. ​​为什么从 i*i 开始?​

i*i 开始,可以确保:

  • 所有比 i*i 小的合数,都已经被比 i 更小的质数标记过了。
  • 只有 i*i 及更大的倍数才可能以 i 为最小质因数。

​例子​​(i=5):

  • 5 * 5 = 25(最小质因数是5,之前未被标记)
  • 5 * 6 = 30(最小质因数是2,但 spf[30] 已经被 i=2 标记为2,所以 i=5 不会真正标记它)
  • 5 * 7 = 35(最小质因数是5,因为7 > 5,且35之前未被标记)

​关键点​​:

  • 如果 j = k*ik < i),那么 j 的最小质因数一定 ≤ k(因为 k 可能是合数,但它的质因数一定比 i 小)。
  • 因此,i 只需要从 i*i 开始标记,因为更小的倍数已经被更小的质数处理过了。

3. ​​数学证明​

假设 j = k*i,其中 k < i

  • 如果 k 是质数,那么 spf[j] 已经被 k 标记过了(因为 k < i)。
  • 如果 k 是合数,那么 k 的最小质因数 p ≤ k < i,所以 spf[j] 已经被 p 标记过了。

因此,i 只需要从 i*i 开始标记,因为更小的 j = k*i 已经被更小的质数处理了。


4. ​​对比埃拉托斯特尼筛法(普通筛法)​

  • ​普通筛法​​:从 2*i 开始标记,因为它只需要知道某个数是否是合数,不关心最小质因数。
  • ​SPF筛法​​:从 i*i 开始,因为要确保 spf[j] 只被最小的质因数标记。

5. ​​例子分析​

max_num = 30 为例,i=5 时的标记过程:

  • 5 * 5 = 25 开始:spf[25] = 5(因为之前未被标记)
  • 下一个 5 * 6 = 30spf[30] 已经被 i=2 标记为2,所以跳过。

如果从 2 * 5=10 开始:

  • spf[10] 已经是2(正确,因为2 < 5),不需要 i=5 处理。
  • spf[15] 已经是3(正确,因为3 < 5),不需要 i=5 处理。
  • spf[20] 已经是2(正确,因为2 < 5),不需要 i=5 处理。

总结

i*i 开始是为了:

  1. ​避免重复标记​​:确保 spf[j] 只被最小的质因数标记。
  2. ​提高效率​​:减少不必要的操作,让算法更高效。
  3. ​正确性保证​​:数学上可以证明,所有比 i*i 小的合数都已经被更小的质数处理过了。
http://www.xdnf.cn/news/18675.html

相关文章:

  • SOC估算方法-蜣螂优化算法结合极限学习
  • NVIDIA Nsight Systems性能分析工具
  • 【Linux系统】进程信号:信号的处理
  • 【基础-判断】订阅dataReceiveProgress响应事件是用来接收HTTP流式响应数据。
  • 基于LLM的跨架构物联网静态漏洞挖掘检测 摘要
  • Ubuntu2204server系统安装postgresql14并配置密码远程连接
  • 小程序备案话术
  • 关于微服务下的不同服务之间配置不能通用的问题
  • pid自适应调节实战设计-基于输出电流的PI参数切换方案
  • React Hooks原理深潜:从「黑魔法」到「可观测」的蜕变之旅
  • Linux服务器Systemctl命令详细使用指南
  • DeepSeek V3.1 横空出世:重新定义大语言模型的边界与可能
  • 水体反光 + 遮挡难题破解!陌讯多模态融合算法在智慧水务的实测优化
  • 深入理解纹理与QtOpenGL的实现
  • 深度集成Dify API:基于Vue 3的智能对话前端解决方案
  • GitHub 热榜项目 - 日榜(2025-08-23)
  • Git的下载安装和使用以及和IDEA的关联
  • 微服务概述1
  • 【K8s】微服务
  • Claude Code快捷键介绍(Claude Code命令、Claude Code指令、Claude Code /命令、Claude命令、Claude指令)
  • P9246 [蓝桥杯 2023 省 B] 砍树
  • 学习嵌入式第三十六天
  • JAVA国际版东郊到家同城按摩服务美容美发私教到店服务系统源码支持Android+IOS+H5
  • PCB电路设计学习3 电路原理图设计 元件PCB封装设计与添加
  • Day12 数据统计-Excel报表
  • 数据结构——树状数组(Binary Indexed Tree)
  • UE5多人MOBA+GAS 53、测试专属服务器打包和连接,以及配置EOS
  • WiFi有网络但是电脑连不上网是怎么回事?该怎么解决?
  • 云原生高级——K8S总概
  • OpenHands:开源AI软件开发代理平台的革命性突破