Loading

数论-整除分块

数论-整除分块

这个蒟蒻太蒻了,希望这篇文章能成为自己恶补数论的开始。

参考资料

https://blog.csdn.net/beautiful_CXW/article/details/83143756


跳转按钮

\(\texttt{讲解证明}\)


\(\texttt{代码实现}\)


\(\texttt{经典例题}\)


\(\texttt{讲解证明}\)

整除分块就是用来求像

\[\sum\limits_{i=1}^n\lfloor \frac{n}{i}\rfloor \]

这样的式子的。

很明显,直接求要 \(\Theta(n)\),但是整除分块只需要 \(\Theta(\sqrt n)\)

整除分块的第一步是发现不同的 \(\lfloor \frac{n}{i}\rfloor\) 的数量

如果 \(i\le\sqrt n\)

很明显,因为 \(i\) 最多 \(\sqrt n\) 种,所以 \(\lfloor \frac{n}{i}\rfloor\) 最多 \(\sqrt n\) 种。

如果 \(i>\sqrt n\)

因为 \(\frac{n}{i}<\sqrt n\),所以 \(\lfloor \frac{n}{i}\rfloor\) 也不到 \(\sqrt n\) 种。

总结:不同的 \(\lfloor \frac{n}{i}\rfloor\) 不到 \(2\sqrt n\) 种。

第二步是计算答案。因为 \(f(i)=\lfloor \frac{n}{i}\rfloor\) 的单调性,所以 \(\lfloor \frac{n}{i}\rfloor\) 相同的 \(i\) 是相邻的

显而易见的结论:对于 \(\lfloor \frac{n}{i}\rfloor=d\)\(i\in(\lfloor\frac{n}{d+1}\rfloor,\lfloor\frac{n}{d}\rfloor]\)

比如 \(n=100,d=6\)。所以 \(i\in(14,16]\)

所以可以以 \(l=\lfloor\frac{n}{d+1}\rfloor+1,r=\lfloor\frac{n}{d}\rfloor\) 为循环变量,

\[l=\texttt{上一次的}r+1,r=\lfloor\frac{n}{\lfloor\frac{n}{l}\rfloor}\rfloor \]


\(\texttt{代码实现}\)

讲解证明一定要仔细看,要不然代码是看不懂的。特短。必须要全局开 \(\texttt{long long}\),这代码可是要过 \(n=10^{12}\) 的数据的!

code

#include <bits/stdc++.h>
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;
}

\(\texttt{经典例题}\)

[CQOI2007]余数求和
\(G(n,k)=\sum\limits_{i=1}^nk\bmod i\)
数据范围:\(1\le n,k\le 10^9\)


推一下(这总得看得懂吧):

\[\sum\limits_{i=1}^nk\bmod i=nk-\sum\limits_{i=1}^n i\times\lfloor\frac{k}{i}\rfloor \]

\[\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} \]

(首项加末项乘项数除以 \(2\))。


注意了,有可能 \(k<n\)。所以

\[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} \]


code

#include <bits/stdc++.h>
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;
}

我还是太蒻了 QQ图片20200302204356.png祝大家学习愉快!

posted @ 2020-03-08 10:54  George1123  阅读(189)  评论(0编辑  收藏  举报