GCD(2021陕西省赛C题)—整除分块
原题地址:GCD
题意
给你\(l\), \(r\)和\(k\),在\([l, r]\)中任意取\(k\)个数,所有取法所对应的最大公约数一共有多少个数。
\(1 ≤ l ≤ r ≤ 10^{12},2 ≤ k ≤ r - l + 1\)
题解
看似是要求所有最大公约数,其实是让求所有可能的公约数。
证明:设任意一个数\(m = i * j\)是区间\([l, r]\)内的两个数\(x,y\)的最大公约数,那么\(x\)与\(x + i\)的最大公约数即为\(i\),\(x\)与\(x + j\)的最大公约数即为\(j\)。即所有可能的公约数都可以是题意里的最大公约数。
而判断一个数\(x\)是否是\([l, r]\)内的某k个数的公约数,可以用式子\(r/x - (l-1)/x ≥ k\)来判断。
要遍历每个\(x\)是不可能的,这里采用整除分块的方法将时间复杂度降为\(O(\sqrt{n})\)。
整除分块:对于任意的\(i\),满足\(n/i == n/j\)的最大的\(j\)为\(n/(n/i)\)。利用这个结论可以很轻易地算出\(\sum\limits_{i = 1}^{n}{n \over i}\)
for (LL l = 1, r; l <= n; l = r + 1) {
r = n / (n / l);
ans += (r - l + 1) * (n / l);
}
利用整除分块思想即可得出解题代码
代码
#include <bits/stdc++.h>
using namespace std;
#define LL long long
#define _for(i, n) for(LL i = 1, lennn = n; i <= lennn; i++)
#define rep(i, a, b) for(LL i = a, lennn = b; i <= lennn; i++)
#define per(i, a, b) for(LL i = a, lennn = b; i >= lennn; i--)
#define sc(x) scanf("%lld", &x)
#define pf(x) printf("%lld\n", x)
#define pu push_back
#define po pop_back
#define maxn 1000006
#define maxnn 1000000007
#define mp make_pair
LL a[maxn], ma = -1, mi = maxnn;
void sol() {
LL ans = 0;
LL l, r, k;
cin >> l >> r >> k;
for(LL i = 1, j; i <= r; i = j + 1) {
if (i < l) j = min(r / (r / i), (l - 1) / ((l - 1) / i));
else j = r / (r / i);
ans += (j - i + 1) * ((r / i - (l - 1) / i) >= k);
}
pf(ans);
}
int main() {
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
time_t t1 = clock();
#endif
sol();
#ifndef ONLINE_JUDGE
printf("time:%.2lfms\n", 1000.0 * (clock() - t1) / CLOCKS_PER_SEC);
#endif
return 0;
}