路漫漫其修远兮,吾将上下而求索——屈原|

tsrigo

园龄:3年7个月粉丝:15关注:10

2022-06-05 19:32阅读: 217评论: 4推荐: 3

AtCoder Beginner Contest 254 - D - Together Square 题解解释

原题

原文

Let f(N) be the largest square divisor of an integer N.

Then, for integers i and j, i×j is a square number if and only if i×jf(i)×f(j) is a square number.

Since if(i) is indivisible by a prime p twice or more, i×jf(i)×f(j) is a square number if and only if if(i)=jf(j)

Therefore, it is sufficient to find f(i) for each integer i(1iN), and to find if(i).

After checking if each integer from 1 through N is a square number, we can find f(i) by bruteforcing every divisor of i.

The complexity is O(NlogN).

翻译

我们令f(N)表示整数N的因数中, 最大的平方数.

那么, 对于整数 i,j ,当且仅当i×jf(i)×f(j)是一个平方数时, i×j 是一个平方数.

又因为 if(i)不能除以某个质数p两次或者两次以上, 故当且仅当if(i)=jf(j)时, i×jf(i)×f(j)是一个平方数.

因此, 只需对每个整数i(1iN), 寻找对应的f(i),if(i)即可.

在初始化完前N个数的平方数之后, 我们就可以通过枚举 i 的每个除数来寻找f(i).

算法复杂度是O(NlogN).

上面的具体操作解释详见代码

解释

f(x)的含义

对于任意一个数x, 都可进行质因数分解: x=piei, 其中pi为质数. 当 x 为平方数时, piei 中的ei将全为偶数. f(x) 可表示为qiei, 其中ei均为偶数, qipi的一个子集.

if(i)可表示为wi, 其中wi的次数均为1.

e.g.

18=232f(18)=3218f(18)=2

对于整数 i,j ,当且仅当i×jf(i)×f(j)是一个平方数时, i×j 是一个平方数

证明:

充分性:

i×jf(i)×f(j)是一个平方数, 则可i×jf(i)×f(j)表示为piei, 其中ei均为偶数.f(i),f(j) 可表示为qiei,kiei, 其中ei均为偶数.

显然, 它乘上f(i),f(j)之后, 各个因数都将是偶数次方, 故i×j为一个平方数

必要性:

i×j是一个平方数, 则i×j可表示为piei, 其中ei均为偶数.f(i),f(j) 可表示为qiei,kiei, 其中ei均为偶数.

i×jf(i)×f(j)不是一个平方数, 则其必有一个因数的次方为奇数次方, 记其为αt, 其中t为奇数. 使i×jf(i)×f(j)乘上f(i),f(j), 由于f(i),f(j)的各因数均为偶数次方, 因数α的次方的奇偶性不会发生改变, 故此时i×j的质因数分解中, 存在奇数次方的因数, 与其是平方数矛盾, 故原命题成立.

又因为 if(i)不能除以某个质数p两次或者两次以上, 故当且仅当if(i)=jf(j)时, i×jf(i)×f(j)是一个平方数

证明:

充分性:

if(i)=jf(j)时, i×jf(i)×f(j)显然是一个平方数, 其平方根即为if(i)=jf(j)

必要性:

上文提到, if(i)可表示为wi, 其中wi的次数均为1, 而一个平方数的质因数分解中的每个因数的次方都为偶数, 故jf(j)的质因数分解中, 至少需要出现wiei, 其中ei为奇数且只能为1.

此时jf(j)再增加任何一个因数的次数次方, 都不能使 i×jf(i)×f(j)平方数, 故if(i)=jf(j).

由于“当且仅当i×jf(i)×f(j)是一个平方数时, i×j 是一个平方数.”,并且“当且仅当if(i)=jf(j)时, i×jf(i)×f(j)是一个平方数”,故由乘法原理知,我们只需求“满足if(i)×if(i)是平方数的 i 的选法”,答案就是满足条件的 i 的数量的平方.

代码

#include <bits/stdc++.h>
using namespace std;
int main(){
int n;
cin>>n;
vector<bool> sq(n+1,false); // 记录 i 是否为平方数
for(int i=1;i*i<=n;i++) sq[i*i]=true;
vector<vector<int>> d(n+1); // d[i]记录的是 i 的所有因数,当然包括1和它本身
for(int i=1;i<=n;i++){
for(int j=i;j<=n;j+=i) d[j].push_back(i);
}
vector<int> cnt(n+1);
for(int i=1;i<=n;i++){
int f=0;
for(int j=0;j<d[i].size();j++) if(sq[d[i][j]]) f=d[i][j];
// 从小到大枚举因数,最后一个满足条件的就是最大的平方数因数
cnt[i/f]++;
// // 乘法原理,不同的 i ,可能对应同一个i/f(i),它们之间可以任意搭配
}
int ans=0;
for(int i=1;i<=n;i++) ans+=cnt[i]*cnt[i];// 即上面所说的乘法原理
cout<<ans<<endl;
}

本文作者:tsrigo

本文链接:https://www.cnblogs.com/tsrigo/p/16344714.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   tsrigo  阅读(217)  评论(4编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起