题意
给定长度为 n 的数列 a。
定义 pk=∑1≤i,j≤kaimodaj。你需要输出 p1,p2,…,pn。
2≤n≤105,1≤ai≤3×105,保证 a 中的元素互不相同。
初步解法
首先看一眼应该能想到显然的 O(n√nlogn) 的做法。
我们从 1 到 n 计算 pk。对于每个算到的 k,我们只要计算 1,2,…,k−1 与 k 之间产生的贡献。考虑正在计算 i(1≤i≤k−1) 与 k 的贡献。
-
当 ai<ak 时,有 aimodak=ai。我们把小于 ak 的 ai 累加起来,树状数组即可。考虑 akmodai 的值,你会发现这和 P2261 [CQOI2007]余数求和 很相似,所以我们套用它的整除分块,在计算时要用树状数组统计符合区间的个数。此部分复杂度 O(√nlogn)。
-
当 ai>ak 时,有 akmodai=ak。我们求出大于 ak 的 ai 个数,树状数组即可。考虑 aimodak 的值,aimodak=ai−⌊aiak⌋×ak,所以我们只要计算 ⌊aiak⌋ 的和。考虑区间 [ak×j,ak×(j+1)−1] 之间的数贡献为 j,所以枚举每个区间,树状数组计算个数。由于 a 互不相同,复杂度是均摊 O(log2n) 的。
总复杂度 O(n√nlogn)。提交上去!
Time limit exceeded on pretest 7 [pretests]
优化解法
发现瓶颈是整除分块的 √n,考虑把它替换掉。也就是考虑 akmodai(ak>ai) 的另一种算法。注意到正着做是 O(√nlogn),倒着却 O(log2n)。那么能不能在计算 ai 的时候就算好 ak 呢?自然是可以的。
在计算 ai 的时候,再用一个数据结构维护桶,与上面的方式类似,找到 [ai×j,ai×(j+1)−1] 区间,容易发现这一段区间 ai 的贡献是 −ai×j,所以对这一段区间加上 ai×j。在到 ak 时,查询一下 ak 这个点的权值,在答案中减掉即可。
容易发现区间总个数仍然是 O(nlogn) 级别。这个数据结构要求支持区间加,单点查,还是树状数组即可。这部分的复杂度由 O(√nlogn) 优化到 O(log2n)了。
Accepted
巨丑无比的代码:link。为了快速用了一个树状数组和一个线段树,主要内容在 main 函数中。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 张高兴的大模型开发实战:(一)使用 Selenium 进行网页爬虫
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构