约数和问题 蜀山 2020 第三题
题解1: 解法1:直接枚举
本题的数据范围很大,想想直接单for枚举会怎么样?
解法1:直接枚举
单for解法
#include<bits/stdc++.h>
using namespace std;
int main()
{
freopen("number.in","r",stdin);
freopen("number.out","w",stdout);
int t,n;
int i,j,ans=0;
scanf("%d",&t);
for(i=1;i<=t;i++)
{
ans=0;
scanf("%d",&n);
for(j=1;j<=n;j++)
{
if(n%j==0)
{
ans+=j;
}
}
printf("%d\n",ans);
}
fclose(stdin);
fclose(stdout);
return 0;
}
不仅枚举要一个循环,输入也要一个循环(说白了就是双for),循环次数是: 次(也就是25后面加上11个0,13位,肯定超了)
那么怎么办呢?
解法2:优化至一半
众所周知,约数都是成对出现的,所以在累加过程中可以把与其相对的那个数也加上(如:15=3*5,加上3的时候顺便加上5)
if语句实现:
if(n%j==0)
{
ans+=j;//加上这个数
ans+=n/j;//顺便加上相对的数
}
这样可以省时间,但省的不多,仍有超时的可能
解法3【最优解】:优化至平方根(也就是)
学过质数判断优化的同学一定知道:一个数的最大因数绝对不会超过自己的平方根(它自己除外)。由此可以推出:约数对的极限就是,所以我们可以把循环优化至,这样节省时间最多,保证不会超时。
最后提醒:ans 一定要开 long long
重要:要考虑到类似于的情况
最终代码如下:
#include<bits/stdc++.h>
using namespace std;
int main()
{
freopen("number.in","r",stdin);
freopen("number.out","w",stdout);
int t,n;
int i,j,ans=0;
scanf("%d",&t);
for(i=1;i<=t;i++)
{
ans=0;
scanf("%d",&n);
for(j=1;j<=sqrt(n);j++)
{
if(n%j==0)
{
if(n/j==j)
{
ans+=j;
}
else
{
ans+=j;
ans+=n/j;
}
}
}
printf("%d\n",ans);
}
fclose(stdin);
fclose(stdout);
return 0;
}
打表法
#include<iostream>
using namespace std;
long long dp[5000010];
int N=5000000,n,t;
int main(){
for(int i=2;i<=N;i++){
for(int j=1;j*i<=N;j++){
dp[i*j]+=i;
}
}
scanf("%d",&t);
while(t--){
scanf("%d",&n);
printf("%d\n",dp[n]+1);//1是任何一个数字的约数
}
return 0;
}
张老师题解:
第三题 :洛谷提交通过。
#include<bits/stdc++.h>
using namespace std;
long long int a[5000005];
int main(){
int t,n,i,j;
for(i=1;i<=5000000;i++){ //所有数是自己的倍数,为以下外循环折半准备
a[i]=i;
}
for(i=1;i<=2500000;i++){ //类筛选求质数,求 i 的倍数,故循环为500万的一半即可
for(j=2*i;j<=5000000;j+=i){ // j 为 i 的倍数
a[j]+=i;
}
}
cin>>t;
for(i=1;i<=t;i++){ //直接输入输出,爽。
scanf("%d",&n);
printf("%lld\n",a[n]);
}
return 0;
}
关注哦