题意:
在给定N内选择两数组成一个有序对<i,j>,要求 i * j 为完全平方数,求有序对个数
思路:
初始思路是我们枚举每个完全平方数的因子,对于2e5的n来说,完全平方达到4e10,那么前一半平方根内的因子还是2e5量级,所以复杂度相当于O(n^2)会TLE。
那么我们尝试只是枚举 i 的因子,利用 i 的因子构造出所有 i^2 的前一半平方根以内的因子,即 i 以内所有 i^2的因子。
我们以n = 6为例,那么最大的完全平方就是36,36的前一半平方根内的因子就是1, 2, 3, 4, 6,而我们想通过,6直接就能找出这五个因子,6的因子只有1,2,3,6没有4,但是6的因子,两两相乘就能得到2 * 2= 4,这样36的前一半因子就构造完毕了。
所以我们先找出 i 的因子,为了避免后续找重我们打标记:
for(LL i = 1; i <= n; i++) {
LL dd = i * i;
vis.clear();
int t = 0;
for(LL j = 1; j <= sqrt(i); j++) { //i的因子
if(i % j == 0) {
if(!vis[j]) {
p[++t] = j;
vis[j] = 1;
}
if(!vis[i / j]) {
p[++t] = i / j;
vis[i / j] = 1;
}
}
}
这时候 i 的因子就全部找到了,然后我们用刚才说的用 i 的因子两两相乘得到 i 以内,i^2的全部因子,此时pr存的即为所求,我们可以测一下pr的输出验证是否是想要的结果
for(int j = 1; j <= t; j++) pr[j] = p[j];
int id = t;
for(int j = 1; j <= t; j++) { //利用i的因子找出i^2的平方根i以前的所有因子
for(int k = j; k <= t; k++) {
LL tmp = p[j] * p[k];
if(tmp <= i && !vis[tmp]) {
pr[++id] = tmp;
vis[tmp] = 1;
}
}
}
最后我们以数组的形式存储每个ans,因为满足 i 的那些ans必然也满足i + 1,所以我们对ans实现累加,我们另dd = i * i,因为 pr[i] 是 i 以内的,又是在 n 内枚举的 i,如果dd / pr[i] 也是n以内的,我们就能构造两个有序对,比如<1,4>和<4,1>,但是如果是<2,2>也就是pr[i]此时正好是平方根,那么有序对个数需要减去1,最后我们当前 i 所能实现完全平方的的有序对个数按上述计算方法记为res,在累加上一个 i 的ans,即为当前 i 的 ans:
LL res = 0;
for(int j = 1; j <= id; j++) {
if(dd / pr[j] <= n) res += 2;
if(dd / pr[j] == pr[j]) res--;
}
ans[i] = ans[i - 1] + res;
}
AC代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxx = 2e5 + 7;
LL n, ans[maxx], p[maxx], pr[maxx];
map <LL, bool> vis;
int main() {
cin >> n;
for(LL i = 1; i <= n; i++) {
LL dd = i * i;
vis.clear();
int t = 0;
for(LL j = 1; j <= sqrt(i); j++) { //i的因子
if(i % j == 0) {
if(!vis[j]) {
p[++t] = j;
vis[j] = 1;
}
if(!vis[i / j]) {
p[++t] = i / j;
vis[i / j] = 1;
}
}
}
for(int j = 1; j <= t; j++) pr[j] = p[j];
int id = t;
for(int j = 1; j <= t; j++) { //利用i的因子找出i^2的平方根i以前的所有因子
for(int k = j; k <= t; k++) {
LL tmp = p[j] * p[k];
if(tmp <= i && !vis[tmp]) {
pr[++id] = tmp;
vis[tmp] = 1;
}
}
}
/*
//pr应该表示当前完全平方数的前一半平方根内的所有因子
cout << dd << ": ";
for(int j = 1; j <= id; j++) {
if(j < id) cout << pr[j] << ' ';
else cout << pr[j] << endl;
}
*/
LL res = 0;
for(int j = 1; j <= id; j++) {
if(dd / pr[j] <= n) res += 2;
if(dd / pr[j] == pr[j]) res--;
}
ans[i] = ans[i - 1] + res;
}
cout << ans[n] << endl;
return 0;
}