[ABC254D] Together Square 题解
题目链接:[ABC254D] Together Square
题意:
给定 $n$,求满足以下条件的二元组 $(i, j)$ 数量:$1 \le i, j \le n, i \times j = k^2(k \in \mathbb{N}^*)$。
$1 \le n \le 2 \times 10^5$
先来讲讲常规做法:
记 $f_i$ 为 $i$ 最大的完全平方数因子,然后我们可以得到这样一个式子
整理一下可得
统计一下 $\frac{i}{f_i}$ 即可。 时间复杂度 $\mathcal{O}(n \sqrt{n})$ 或者用筛法可以到 $\mathcal{O}(n \log n)$
代码:
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
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)(2e5 + 15))
int vis[N];
int f(int x)
{
int res = 0;
for(int i = 1; i * i <= x; i++)
if(x % i == 0) {
int j = x / i, a = sqrt(i), b = sqrt(j);
if(a * a == i) up(res, i); if(b * b == j) up(res, j);
}
return res;
}
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, res = 0; cin >> n;
for(int i = 1; i <= n; i++) ++vis[i / f(i)];
for(int i = 1; i <= n; i++) res += vis[i / f(i)];
cout << res << '\n';
return 0;
}
然后来讲讲更快的 $\mathcal{O}(\sqrt{n})$ 的解法。
考虑讲题目要枚举的 $i,j$ 拆成 $i=a^2\times c,~j = b^2\times c$ ,暂且仅考虑 $i < j$
由 $i < j < n$ 得
即
对于确定的 $a,b$ ,显然有 $1 \le c \le \frac{n}{b^2}$ ,于是它的贡献似乎是 $\left\lfloor\frac{n}{b^2}\right\rfloor$ 。
事实上,如果 $a,b$ 不互质,则显然会被 $a^{\prime} = \frac{a}{\gcd(a,b)}$ 的枚举包含,因此贡献是
这个互质真是非常巧妙,以至于刚好满足了 $\varphi(b)$ 的定义。
于是我们只需要枚举 $i(i^2 \le n)$ ,每次加上 $\varphi(i)\times \left\lfloor\frac{n}{i^2}\right\rfloor$ 即可。
时间复杂度 $\mathcal{O}(\sqrt{n})$
代码:
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
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)(2e6 + 15))
int L,phi[N]; char ck[N]; vector<int> P;
void Euler(int n)
{
ck[1] = true; phi[1] = 1;
for(int i = 2; i <= n; i++)
{
if(!ck[i]) { P.push_back(i); phi[i] = i - 1; }
for(auto p : P) {
int pos = i * p; ck[pos] = 1;
if(i % p) { phi[pos] = phi[i] * phi[p]; }
else { phi[pos] = phi[i] * p; break; }
}
}
}
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, res = 0; cin >> n; L = sqrt(n); Euler(L + 5);
for(int i = 1; i * i <= n; i++) res += phi[i] * (n / (i * i));
cout << (res * 2 - n) << '\n';
return 0;
}
参考文献: