洛谷P4466 [国家集训队] 和与积 题解
题目链接:P4466 [国家集训队] 和与积
题意:
给出 $n$,统计满足下面条件的数对 $(a,b)$ 的个数:
- $1\le a<b \le n$。
- $a+b$ 整除 $a\times b$ ,即 $(a + b) \mid ab$。
输入格式:
一行一个整数数 $n$。
输出格式:
一行一个整数表示答案。
数据范围:
$1 \le n < 2^{31}$ 。
设 $d=\gcd(a,b)$ ,记 $a=xd,~b=yd$ ,则
由 $\gcd(x,y)=1$ 可知 $\gcd(y, x + y)=1$ ,则 $\gcd(xy, x + y)=1$ 。
再由 $\frac{xyd}{x + y} \in \mathbb{N}$ 可知 $(x + y) \mid d$ ,显然 $x + y \le d$ 。
又因为 $xd \le n, yd \le n$ 且 $x < y$ ,所以 $d \le \left\lfloor \frac{n}{y}\right \rfloor$ 。
那么答案就是下式:
令 $s = x+y$ ,则
虽然这个式子非常毒瘤,但是仔细看看又确实可以数论分块。
来分析一下复杂度:
记
直接算就是
提示:这里用到了 link 中提到的公式
因为
所以复杂度是
后面的东西就是
因为 $n \ge 1$ ,所以这个东西不会超过 $2$ 。
因此时间复杂度为 $\mathcal{O}(n^{\frac{3}{4}})$ 。
代码:
#include <bits/stdc++.h>
using namespace std;
// #define int long long
// #define INF 0x3f3f3f3f3f3f3f3f
typedef long long ll;
void up(int &x,int y) { x < y ? x = y : 0; }
void down(int &x,int y) { x > y ? x = y : 0; }
#define N ((int)(5e5 + 15))
char ck[N];
int cnt, mu[N], pri[N]; ll res;
void Mobius(int n)
{
mu[1] = 1;
for(int i = 2; i <= n; i++)
{
if(!ck[i]) { pri[++cnt] = i, mu[i] = -1; }
for(int j = 1; j <= cnt && pri[j] * i <= n; j++)
{
int pos = pri[j] * i; ck[pos] = 1;
if(i % pri[j]) { mu[pos] = -mu[i]; } else break;
}
}
}
int calc(int s, int k)
{
int sum = 0;
for(int l = s + 1, r; l < 2 * s; l = r + 1)
{
if(!(k / l)) return sum;
r = min(k / (k / l), 2 * s - 1);
sum += (r - l + 1) * (k / l);
}
return sum;
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
// freopen("check.in","r",stdin);
// freopen("check.out","w",stdout);
int n; cin >> n; int sqn = sqrt(n); Mobius(sqn);
for(int i = 1; i <= sqn; i++)
if(mu[i] != 0) {
for(int j = 1; j <= sqn / i; j++)
res += mu[i] * calc(j, n / (i * i * j));
}
cout << res << '\n';
return 0;
}
参考文献:
[1] https://www.luogu.com.cn/article/m278tivk
[2] https://www.luogu.com.cn/article/2k25fj3f
[3] https://www.luogu.com.cn/article/gzvll5kq
[4] https://www.luogu.com.cn/article/762ia004
题外话:
还有个 $\mathcal{O}(n^{\frac{2}{3}})$ 的做法,但是常数比较大(而且我也不会)就不讲了