【ICPC】2021台湾 B. Aliquot Sum | 筛法求因数和

185 阅读2分钟

本文已参与「新人创作礼」活动, 一起开启掘金创作之路。

【ICPC】2021台湾 B. Aliquot Sum | 筛法求因数和

题目链接

Problem - B - Codeforces

题目

image.png

题目大意

定义函数 s(x)s(x) 表示自身之外 xx 所有因数的和,判断 s(x)s(x)xx 的大小关系:

  • s(x)>xs(x)>x,输出 abundant
  • s(x)<xs(x)<x,输出 deficient
  • s(x)=xs(x)=x,输出 perfect

思路

赛中一眼开始写筛法,飞速敲完 WA2……
队友发现时限非常宽于是写了对每组询问 O(nn)O(n\sqrt{n}) 暴力求因数和喜提 TLE,改成把 1e6 范围内所有的数的因数和用 O(nn)O(n\sqrt{n}) 的时间预处理出来,对每个询问 O(1)O(1) 回答,龟速通过了此题qaq

补题发现自己的筛法预处理的上限设小了(x

暴力解法比较显然,这里只简单介绍筛法的思路。我们需要在一个普通欧拉筛的基础上进行修改……然后对于每个数字 xx 我们进行如下讨论:

当我们遍历到 xxxx 还没有被打上标记,即 xx 是质数,那么 s(x)=1s(x)=1。否则 xx 一定会被它的最小素因子给筛掉,即 x=i×pjx=i\times p_j,其中 pjp_jxx 的最小素因子。

此时如果 pjip_j\nmid i,则说明

  • ii 的所有因数都是 xx 的因数。
  • ii 的任一因数乘 pjp_j 的积都不是 ii 的因数。
  • ii 的任一因数乘 pjp_j 的积都是 xx 的因数。

ii 不存在于 s(i)s(i) 且是 xx 的因数,所以在这种情况下 s(x)=s(i)+s(i)×pj+is(x)=s(i)+s(i)\times p_j+i

此时如果 pjip_j\mid i,则只有在 pjp_j 取仍是 ii 的因数的最高次幂,才不是 ii 的因数。 令 kk 合理取值使得 (pjkx)(pjk+1x)(p_j^k\mid x)\land(p_j^{k+1}\nmid x),则 s(x)=s(i)+s(xpjk)pjk+is(x)=s(i)+s(\frac{x}{p_j^k})*p_j^k+i

综上,修改欧拉筛即可在 O(nlogn)O(n\log{n}) 时间里求出 11nn 中每个数的因数和。

按题意判断输出答案即可。

代码

暴力求因数和 O(nn)O(n\sqrt{n})

#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");
	}
}

筛法求因数和 O(nlogn)O(n\log{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;
}