整除分块
整除分块
前言
本文为莫比乌斯反演的必备前置知识,同时在许多数论题目中都有应用,建议认真阅读
1.知识概述
整除分块适用于形似这个类型的式子 $${\sum_{i=1}^{n}{\lfloor\frac{n}{i}\rfloor}}$$
通过我们缜密的数学思想(打表)可以得知
n=10
i 1 2 3 4 5 6 7 8 9 10
n/i 10 5 3 2 2 1 1 1 1 1
\({\lfloor\frac{n}{i}\rfloor}\)这个式子的值是成块状分布
也就是是说,我们可以将每一块打包一起处理
问题来了,每一块的边界怎么求呢?
容易想到,对于每一个整块,假设我们知道这一块的右端点,那么下一块的左端点一定是这一块的右端点的右边第一个数。
于是问题转化为怎么求一个块的右端点
观察可以发现,每一块的最后一个数一定是可以被整除的
而一个块中的每一个数被n除后的取整结果都相同
假设我们知道第i块的左端点l[i],那么第i块的右端点数应该为$${\lfloor\frac{n}{{\lfloor\frac{n}{l[i]}\rfloor}}\rfloor}$$
复杂度为O(\({\sqrt{n}}\))。
2.例题
luoguP2261 [CQOI2007]余数求和
这题比较简单,先推下式子
\[G(n, k) = \sum_{i = 1}^n k \bmod i
\]
对于每一个i,可以发现要求的值应该是
\[k\bmod i=k-\lfloor\frac{k}{i}\rfloor*i
\]
求一下和就是
\[G(n, k) = \sum_{i = 1}^n k-\lfloor\frac{k}{i}\rfloor*i
\]
先把\(k\)提出来就是\(n*k\),剩下的直接整除分块就行,至于\(i\),直接用等差数列求和公式求。
假设有\(m\)块,整理下公式就是这样
\[G(n, k) = n*k-\sum_{j = 1}^m\frac{(r[j]*r[j]+r[j])-(l[j]*l[j]-l[j])}{2}*\lfloor\frac{k}{l[j]}\rfloor
\]
注意循环中的边界处理。
Coding:
#include<bits/stdc++.h>
#define ll long long
#define f1(i,n,m) for(int i=n;i<=m;++i)
using namespace std;
ll n,k,p=1,l,r;
ll ans;
ll sum(ll l,ll r){
return 1ll*r*(r+1)/2-1ll*(l-1)*l/2;
}
signed main(){
cin>>n>>k;
ans+=n*k;
while(p<=n){
l=p;
if(k/p!=0)r=min(k/(k/p),n);
else r=n;
ans-=sum(l,r)*(k/p);
p=r+1;
}
printf("%lld\n",ans);
}