ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

【学习笔记】整除分块

2020-07-06 11:04:41  阅读:550  来源: 互联网

标签:lfloor frac limits 分块 sum rfloor 笔记 整除 ll


问题

给定整数 \(n,p(1\le n\le 10^{12})\),求 \(\sum\limits_{i=1}^{n}\lfloor \frac{n}{i}\rfloor\bmod p\)。

思路

暴力做肯定会超时。我们发现加数中有许多是相同的,并且这些加数单调不增(即相同加数必定在一起)。如果找出每段相同加数的长度,就能很快得到答案。

也就是说,如果对于一个 \(i\),能找出最大的 \(j\) 使得 \(\lfloor \frac{n}{i}\rfloor=\lfloor\frac{n}{j}\rfloor\)
,这一段的和即为 \(\lfloor\frac{n}{i}\rfloor(j-i+1)\)。这样就能大大提高效率。

根据向下取整的性质,可得 \(\lfloor \frac{n}{i}\rfloor\le\frac{n}{j}<\lfloor \frac{n}{i}\rfloor+1\)

将前一个不等式变形,得 \(j\le \frac{n}{\lfloor \frac{n}{i}\rfloor}\)

由于 \(j\) 是正整数,可得 \(j\le \big\lfloor\frac{n}{\lfloor \frac{n}{i}\rfloor}\big\rfloor\)

所以 \(j\) 最大为 \(\big\lfloor\frac{n}{\lfloor \frac{n}{i}\rfloor}\big\rfloor\)。

代码

for(i = 1; i <= n; i = j + 1) {
	j = n / (n / i);
	ans += (j - i + 1) * (n / i) % p;
}

复杂度证明

  • 当 \(1\le i\le \sqrt n\) 时:\(i\) 的取值有 \(\sqrt n\) 种,所以此时 \(\lfloor\frac{n}{i}\rfloor\) 的取值最多有 \(\sqrt n\) 种。

  • 当 \(\sqrt n< i\le n\) 时:\(1\le\lfloor\frac{n}{i}\rfloor\le\sqrt n\),所以 \(\lfloor\frac{n}{i}\rfloor\) 的取值最多有 \(\sqrt n\) 种。

也就是说,\(\lfloor\frac{n}{i}\rfloor\) 的取值最多有 \(2\sqrt n\) 种,所以复杂度为 \(\mathcal{O}(\sqrt n)\)。

进阶

注:下列各式中 \(\bmod p\) 已省略。

  • 求 \(\sum\limits_{i=1}^{\min(n,m)}\lfloor \frac{n}{i}\rfloor\lfloor\frac{m}{i}\rfloor\)

每次右边界取 \(\min(\big\lfloor\frac{n}{\lfloor \frac{n}{i}\rfloor}\big\rfloor,\big\lfloor\frac{m}{\lfloor \frac{m}{i}\rfloor}\big\rfloor)\),保证了两者分别相等,所以每两项的乘积相等。复杂度并没有变化。

  • 求 \(\sum\limits_{i=1}^{n}i\lfloor\frac{n}{i}\rfloor\)

对于每个块: \(\sum\limits_{i=l}^{r}i\lfloor \frac{n}{i}\rfloor=\sum\limits_{i=l}^{r}i\lfloor \frac{n}{l}\rfloor=\lfloor \frac{n}{l}\rfloor\sum\limits_{i=l}^{r}i=\lfloor \frac{n}{l}\rfloor\times\frac{(r-l+1)(l+r)}{2}\)

  • 求 \(\sum\limits_{i=1}^{n}\lceil\frac{n}{i}\rceil\)

\(\lceil\frac{n}{i}\rceil=\begin{cases}\lfloor\frac{n}{i}\rfloor&i\mid n\\\lfloor\frac{n}{i}\rfloor+1&i\nmid n\end{cases}\)

一共有 \(d(n)\) (表示 \(n\) 的因数个数)个数满足 \(i\mid n\),我们计算全部都不能整除时的结果,最后减去因数个数。

\(\text{原式}=\sum\limits_{i=1}^{n}(\lfloor\frac{n}{i}\rfloor+1)-d(n)=n-d(n)+\sum\limits_{i=1}^{n}\lfloor\frac{n}{i}\rfloor\)

  • 求 \(\sum\limits_{i=1}^{n}\lfloor\frac{n}{i^2}\rfloor\)

令 \(l'=l^2\),\(r'=r^2\),由那个最基本的柿子可以得到 \(r'=\big\lfloor\frac{n}{\lfloor \frac{n}{l'}\rfloor}\big\rfloor\)。

所以 \(r=\Big\lfloor\sqrt{\big\lfloor\frac{n}{\lfloor \frac{n}{l^2}\rfloor}\big\rfloor}\Big\rfloor\)

  • 已知 \(n,a,b\) 为正整数,求 \(\sum\limits_{i=1}^{n}\lfloor\frac{n}{ai+b}\rfloor\)

令 \(l'=al+b\),\(r'=ar+b\),由那个最基本的柿子可以得到 \(r'=\big\lfloor\frac{n}{\lfloor \frac{n}{l'}\rfloor}\big\rfloor\)。

所以 \(ar+b=\big\lfloor\frac{n}{\lfloor \frac{n}{l'}\rfloor}\big\rfloor\)

\(r=\Big\lfloor\dfrac{\big\lfloor\frac{n}{\lfloor \frac{n}{l'}\rfloor}\big\rfloor-b}{a}\Big\rfloor\)

一般地,对于求 \(\sum\limits_{i=1}^{n}\lfloor\frac{k}{f(i)}\rfloor\bmod p(\text{其中}n,p,k\text{是正整数},1\le n\le10^{12})\) 时,块边界推导方法是:

\(\lfloor \frac{k}{f(l)}\rfloor=\lfloor \frac{k}{f(r)}\rfloor\),由那个最基本的柿子可知 \(f(r)=\big\lfloor\frac{n}{\lfloor\frac{n}{f(l)}\rfloor}\big\rfloor\)。

求出 \(f(l)\),然后再解这个关于 \(r\) 的方程,最后向下取整,就得到了该块的右边界。

例题

模板题,直接上代码。

q = read();
for(; q; --q) {
	ans = 0;
	n = read();
	for(i = 1; i <= n; i = j + 1) {
		j = n / (n / i);
		ans += (j - i + 1) * (n / i);
	}
	printf("%lld\n", ans);
}

先来推柿子:\(\sum\limits_{i=1}^nk\bmod i=\sum\limits_{i=1}^n(k-i\lfloor\frac{k}{i}\rfloor)=nk-\sum\limits_{i=1}^ni\lfloor\frac{k}{i}\rfloor\)

对于每个块:\(\sum\limits_{i=l}^ri\lfloor\frac{k}{i}\rfloor=\sum\limits_{i=l}^ri\lfloor\frac{k}{l}\rfloor=\lfloor\frac{k}{l}\rfloor\sum\limits_{i=l}^ri=\frac{\lfloor\frac{k}{l}\rfloor(l+r)(r-l+1)}{2}\)

代码:

scanf("%lld%lld", &n, &k);
for(i = 1; i <= n && k / i; i = j + 1) {
	j = min(n, k / (k / i));
	ans += (i + j) * (j - i + 1) * (k / i);
}
printf("%lld\n", n * k - (ans >> 1));

题目中要求 \(i\neq j\),不太好做,我们可以先算出没有这一限制条件时的答案,再减去当 \(i=j\) 时的答案。

先令 \(n\) 为较小的那个。

\(\text{原式}=\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{m}(n\bmod i)(m\bmod j)-\sum\limits_{i=1}^{n}(n\bmod i)(m\bmod i)\)

\(=[\sum\limits_{i=1}^{n}(n-i\lfloor\frac{n}{i}\rfloor)][\sum\limits_{j=1}^{m}(m-j\lfloor\frac{m}{j}\rfloor)]-\sum\limits_{i=1}^{n}(n-i\lfloor\frac{n}{i}\rfloor)(m-i\lfloor\frac{m}{i}\rfloor)\)

\(=(n^2-\sum\limits_{i=1}^{n}i\lfloor\frac{n}{i}\rfloor)(m^2-\sum\limits_{j=1}^{m}j\lfloor\frac{m}{j}\rfloor)-\sum\limits_{i=1}^{n}(nm-mi\lfloor\frac{n}{i}\rfloor-ni\lfloor\frac{m}{i}\rfloor+i^2\lfloor\frac{n}{i}\rfloor\lfloor\frac{m}{i}\rfloor)\)

\(=(n^2-\sum\limits_{i=1}^{n}i\lfloor\frac{n}{i}\rfloor)(m^2-\sum\limits_{j=1}^{m}j\lfloor\frac{m}{j}\rfloor)-n^2m+m\sum\limits_{i=1}^{n}i\lfloor\frac{n}{i}\rfloor+n\sum\limits_{i=1}^{n}i\lfloor\frac{m}{i}\rfloor-\sum\limits_{i=1}^{n}i^2\lfloor\frac{n}{i}\rfloor\lfloor\frac{m}{i}\rfloor\)

每个和式都可以整除分块。

注意题目中模数不是质数,可用 exgcd 求逆元。

代码:

#include <cstdio>
#include <cstring>

using namespace std;

#define in inline
typedef long long ll;
in ll max(ll x, ll y) {return x > y ? x : y;}
in ll min(ll x, ll y) {return x < y ? x : y;}
in void swap(ll &x, ll &y) {x ^= y ^= x ^= y;}
#define rei register int
#define FOR(i, l, r) for(rei i = l, i##end = r; i <= i##end; ++i)
#define FOL(i, r, l) for(rei i = r, i##end = l; i >= i##end; --i)

const int Mod = 19940417;
ll ans, n, m, inv2, inv6, tmp;

in ll MOD(ll x) {return (x % Mod + Mod) % Mod;}

void exgcd(ll a, ll p, ll &x, ll &y) {//exgcd求逆元
	if(!p) {x = 1; y = 0; return;}
	exgcd(p, a % p, x, y);
	ll tmp = x;
	x = y;
	y = tmp - y * (a / p);
}

in ll Sum2(ll n) {return n * (n + 1) % Mod * (n + n + 1) % Mod * inv6 % Mod;}//求1^2+2^2+3^2+...+n^2

ll Sumx(ll n, ll k) {//1*(k/1)+2*(k/2)+...+n*(k/n)
	ll res = 0;
	for(register ll i = 1, j; i <= n && k / i; i = j + 1) {
		j = min(k / (k / i), n);
		res = (res + (i + j) * (j - i + 1) % Mod * (k / i) % Mod) % Mod;
	}
	return res * inv2 % Mod;
}

inline ll SumMod(ll n, ll k) {return (n * k % Mod - Sumx(n, k) % Mod + Mod) % Mod;}//(kmod1)+(kmod2)+...+(kmodn)

ll Sumxy(ll n, ll m) {//最后一个和式
	ll res = 0;
	for(register ll i = 1, j; i <= n; i = j + 1) {
		j = min(n / (n / i) ,m / (m / i));
		res = (res + (n / i) * (m / i) % Mod * MOD(Sum2(j) - Sum2(i - 1))) % Mod;
	}
	return res % Mod;
}

signed main() {
	scanf("%lld%lld", &n, &m);
	if(n > m) swap(n, m);
	exgcd(2, 19940417, inv2, tmp); inv2 = (inv2 % Mod + Mod) % Mod;
	exgcd(6, 19940417, inv6, tmp); inv6 = (inv6 % Mod + Mod) % Mod;
	ans = SumMod(n, n) * SumMod(m, m) % Mod;
	ans = MOD(ans - n * n % Mod * m % Mod);
	ans = (ans + m * Sumx(n, n) % Mod + n * Sumx(n, m) % Mod) % Mod;
	ans = MOD(ans - Sumxy(n, m));
	printf("%lld\n", ans);
	return 0;
}

标签:lfloor,frac,limits,分块,sum,rfloor,笔记,整除,ll
来源: https://www.cnblogs.com/creating-2007/p/13253703.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有