这个蒟蒻太蒻了,希望这篇文章能成为自己恶补数论的开始。
参考资料
https://blog.csdn.net/beautiful_CXW/article/details/83143756
跳转按钮
讲解证明 \color{#8af}\texttt{讲解证明} 讲解证明
代码实现 \color{#8af}\texttt{代码实现} 代码实现
经典例题 \color{#8af}\texttt{经典例题} 经典例题
整除分块就是用来求像
∑ i = 1 n ⌊ n i ⌋ \sum\limits_{i=1}^n\lfloor \frac{n}{i}\rfloor i=1∑n⌊in⌋
这样的式子的。
很明显,直接求要 Θ ( n ) \Theta(n) Θ(n),但是整除分块只需要 Θ ( n ) \Theta(\sqrt n) Θ(n)。
整除分块的第一步是发现不同的 ⌊ n i ⌋ \lfloor \frac{n}{i}\rfloor ⌊in⌋ 的数量。
如果 i ≤ n i\le\sqrt n i≤n:
很明显,因为 i i i 最多 n \sqrt n n 种,所以 ⌊ n i ⌋ \lfloor \frac{n}{i}\rfloor ⌊in⌋ 最多 n \sqrt n n 种。
如果 i > n i>\sqrt n i>n:
因为 n i < n \frac{n}{i}<\sqrt n in<n,所以 ⌊ n i ⌋ \lfloor \frac{n}{i}\rfloor ⌊in⌋ 也不到 n \sqrt n n 种。
总结:不同的 ⌊ n i ⌋ \lfloor \frac{n}{i}\rfloor ⌊in⌋ 不到 2 n 2\sqrt n 2n 种。
第二步是计算答案。因为 f ( i ) = ⌊ n i ⌋ f(i)=\lfloor \frac{n}{i}\rfloor f(i)=⌊in⌋ 的单调性,所以 ⌊ n i ⌋ \lfloor \frac{n}{i}\rfloor ⌊in⌋ 相同的 i i i 是相邻的。
显而易见的结论:对于 ⌊ n i ⌋ = d \lfloor \frac{n}{i}\rfloor=d ⌊in⌋=d, i ∈ ( ⌊ n d + 1 ⌋ , ⌊ n d ⌋ ] i\in(\lfloor\frac{n}{d+1}\rfloor,\lfloor\frac{n}{d}\rfloor] i∈(⌊d+1n⌋,⌊dn⌋]。
比如 n = 100 , d = 6 n=100,d=6 n=100,d=6。所以 i ∈ ( 14 , 16 ] i\in(14,16] i∈(14,16]
所以可以以 l = ⌊ n d + 1 ⌋ + 1 , r = ⌊ n d ⌋ l=\lfloor\frac{n}{d+1}\rfloor+1,r=\lfloor\frac{n}{d}\rfloor l=⌊d+1n⌋+1,r=⌊dn⌋ 为循环变量,
l = 上一次的 r + 1 , r = ⌊ n ⌊ n l ⌋ ⌋ l=\texttt{上一次的}r+1,r=\lfloor\frac{n}{\lfloor\frac{n}{l}\rfloor}\rfloor l=上一次的r+1,r=⌊⌊ln⌋n⌋
。
讲解证明一定要仔细看,要不然代码是看不懂的。特短。必须要全局开 long long \texttt{long long} long long,这代码可是要过 n = 1 0 12 n=10^{12} n=1012 的数据的!
code
#include
using namespace std;
//&Start
#define lng long long
#define lit long double
const int inf=0x3f3f3f3f;
const lng Inf=1e17;
//&Main
lng n,ans;
int main(){
scanf("%lld",&n);
for(lng l=1,r;l<=n;l=r+1)
r=n/(n/l),ans+=(r-l+1)*(n/l);
printf("%lld\n",ans);
return 0;
}
[CQOI2007]余数求和
求 G ( n , k ) = ∑ i = 1 n k m o d i G(n,k)=\sum\limits_{i=1}^nk\bmod i G(n,k)=i=1∑nkmodi。
数据范围: 1 ≤ n , k ≤ 1 0 9 1\le n,k\le 10^9 1≤n,k≤109。
推一下(这总得看得懂吧):
∑ i = 1 n k m o d i = n k − ∑ i = 1 n i × ⌊ k i ⌋ \sum\limits_{i=1}^nk\bmod i=nk-\sum\limits_{i=1}^n i\times\lfloor\frac{k}{i}\rfloor i=1∑nkmodi=nk−i=1∑ni×⌊ik⌋
∑ i = 1 n i × ⌊ k i ⌋ = ∑ l , r n ⌊ k l ⌋ × ( l + r ) ( r − l + 1 ) 2 \sum\limits_{i=1}^n i\times\lfloor\frac{k}{i}\rfloor=\sum\limits_{l,r}^n\lfloor\frac{k}{l}\rfloor\times\frac{(l+r)(r-l+1)}{2} i=1∑ni×⌊ik⌋=l,r∑n⌊lk⌋×2(l+r)(r−l+1)
(首项加末项乘项数除以 2 2 2)。
注意了,有可能 k < n k
r = { min ( ⌊ k ⌊ k l ⌋ ⌋ , n ) ( ⌊ k l ⌋ > 0 ) n ( ⌊ k l ⌋ = 0 ) r= \begin{cases} \min(\lfloor\frac{k}{\lfloor\frac{k}{l}\rfloor}\rfloor,n)~~(\lfloor\frac{k}{l}\rfloor>0)\\ n~~~~~~~~~~~~~~~~~~~~~~~(\lfloor\frac{k}{l}\rfloor=0) \end{cases} r={min(⌊⌊lk⌋k⌋,n) (⌊lk⌋>0)n (⌊lk⌋=0)
。
code
#include
using namespace std;
//&Start
#define lng long long
#define lit long double
const int inf=0x3f3f3f3f;
const lng Inf=1e17;
//&Main
lng n,k,ans;
int main(){
scanf("%lld%lld",&n,&k),ans=n*k;
for(lng l=1,r;l<=n;l=r+1)
r=(k/l)?min(k/(k/l),n):n,ans-=(l+r)*(r-l+1)/2*(k/l);
printf("%lld\n",ans);
return 0;
}
我还是太蒻了 ,祝大家学习愉快!