值域分段求和
假如我们要对这样的式子进行求和:
n∑i=1f(i)
如果 f(i) 的取值有限,只有 m 个,且对于所有的 f(i)=x 在序列中都是连续的一段,那么就可以进行值域分段求和。
找出每个取值 x 在原序列中的第一次出现的位置 Lx 和最后一次出现的位置 Rx,可以在 O(m) 的时间内算出答案(设 v1,v2…vm 为 f(i) 的 m 种取值):
Ans=m∑i=1(Ri−Li+1)vi
例如:
n∑i=1⌊log2i⌋
设 f(i)=⌊log2i⌋,容易发现 0≤f(i)≤log2n 且单调递增。运用上述方法,可计算出 Lx=2x,Rx=2x+1−1,于是可以在 O(logn) 的时间内求解:
Ans=⌊log2(n)⌋∑i=12ii
整除分块
整除分块就是 依据整除的性质,对取值种类只有 √n 级别的 f 序列进行的值域分段求和。
举几个简单的例子:
n∑i=1⌊ni⌋
设 f(i)=⌊ni⌋,则 f(i) 的取值不超过 2√n 种且单调递增。运用上述方法,可计算出 Lx=⌊n⌊nx−1⌋⌋+1,Rx=⌊n⌊nx⌋⌋,值域分段求和即可,时间复杂度 O(√n)。
代码片段:
for(int l=1,r=0; l<=n;) {
l=r+1,r=n/(n/l);
ans+=(n/l)*(r-l+1);
}
n∑i=1i2⌊ni⌋
设 f(i)=⌊ni⌋,仍然有 Lx=⌊n⌊nx−1⌋⌋+1,Rx=⌊n⌊nx⌋⌋。考虑对于每一段 [Lx,Rx] 计算答案的贡献:
Rx∑i=Lxi2x
=xRx∑i=Lxi2
=x(Rx∑i=1i2−Lx−1∑i=1i2)
=x(Rx(Rx+1)(2Rx+1)6−Lx(Lx+1)(2Lx+1)6)
然后就可以 O(√n) 求解了。
代码片段:
for(int l=1,r=0; l<=n;) {
l=r+1,r=n/(n/l);
ans+=(n/l)*((r*(r+1)*(r<<1|1)/6-l*(l+1)*(l<<1|1)/6);
}
习题:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现