本文已参与「新人创作礼」活动, 一起开启掘金创作之路。
【ICPC】2021台湾 B. Aliquot Sum | 筛法求因数和
题目链接
题目
题目大意
定义函数 表示自身之外 所有因数的和,判断 和 的大小关系:
- ,输出
abundant
。 - ,输出
deficient
。 - ,输出
perfect
。
思路
赛中一眼开始写筛法,飞速敲完 WA2……
队友发现时限非常宽于是写了对每组询问 暴力求因数和喜提 TLE,改成把 1e6 范围内所有的数的因数和用 的时间预处理出来,对每个询问 回答,龟速通过了此题qaq
补题发现自己的筛法预处理的上限设小了(x
暴力解法比较显然,这里只简单介绍筛法的思路。我们需要在一个普通欧拉筛的基础上进行修改……然后对于每个数字 我们进行如下讨论:
当我们遍历到 时 还没有被打上标记,即 是质数,那么 。否则 一定会被它的最小素因子给筛掉,即 ,其中 是 的最小素因子。
此时如果 ,则说明
- 的所有因数都是 的因数。
- 的任一因数乘 的积都不是 的因数。
- 的任一因数乘 的积都是 的因数。
而 不存在于 且是 的因数,所以在这种情况下 。
此时如果 ,则只有在 取仍是 的因数的最高次幂,才不是 的因数。 令 合理取值使得 ,则 。
综上,修改欧拉筛即可在 时间里求出 到 中每个数的因数和。
按题意判断输出答案即可。
代码
暴力求因数和
#include <bits/stdc++.h>
using namespace std;
const int N=1e6+5;
int n=100;
using ll = long long;
ll ss[N];
ll s(ll x) {
ll ans = 0;
unordered_map<ll,ll>mp;
for( int i = 2; i <= sqrt(x); i++) {
if(x % i == 0) ans += i;
if(x % i == 0&&i!=x/i) ans += x / i;
}
return ans + 1;
}
int main()
{
for( int i = 1; i <= 1e6; i++) ss[i] = s(i);
scanf("%d",&n);
while (n--)
{
ll x;
scanf("%lld",&x);
if(x == 1) printf("deficient\n");
else if (ss[x]>x) printf("abundant\n");
else if (ss[x]==x) printf("perfect\n");
else printf("deficient\n");
}
}
筛法求因数和
#include <bits/stdc++.h>
using namespace std;
const int N=1e6+5;
int n=1000000;
long long s[N];
int tot,p[N],ntp[N];
int main()
{
for (int i=2;i<=n;++i)
{
if (!ntp[i])
{
p[++tot]=i;
s[i]=1;
}
for (int j=1;j<=tot&&i*p[j]<=n;++j)
{
ntp[i*p[j]]=1;
if (i%p[j]==0)
{
int k=i,pp=p[j];
while (k%p[j]==0)
{
k/=p[j];
pp*=p[j];
}
s[i*p[j]]=s[i]+s[k]*pp+i;
break;
}
else s[i*p[j]]=s[i]+p[j]*s[i]+i;
}
}
scanf("%d",&n);
while (n--)
{
int x;
scanf("%d",&x);
if (s[x]>x) printf("abundant\n");
if (s[x]==x) printf("perfect\n");
if (s[x]<x) printf("deficient\n");
}
return 0;
}