Atcoder Beginner Contest 254D 题解

链接:D - Together Square

题意:

在给定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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值