整除分块

整除分块

前言

本文为莫比乌斯反演的必备前置知识,同时在许多数论题目中都有应用,建议认真阅读

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);
}
posted @ 2022-09-13 21:49  windf_风岚  阅读(79)  评论(0)    收藏  举报
1