您的位置:首页 > 其它

HDU 1215 七夕节(因子和)

2015-09-04 08:32 399 查看
Description

输出一个数的所有因子和(不包括自身)

Input

输入数据的第一行是一个数字T(1<=T<=500000),它表明测试数据的组数.然后是T组测试数据,每组测试数据只有一个数字n(1<=n<=500000).

Output

对于每组测试数据,请输出一个代表输入数据N的另一半的编号.

Sample Input

3

2

10

20

Sample Output

1

8

22

Solution

因为T非常大,所以首先应该预处理出500000以内所有数的因子和,然后O(1)查询,因此限制的最高时间复杂度也不能超过O(nlogn),对n质因数分解得n=p1^k1*p2^k2*…pm^km,那么轻易得到n的因子和为(p1^0+p1^1+…+p1^k1)(p2^0+p2^1+…+p2^k2)…(pm^0+pm^1+…+pm^km),令t=(p1^0+p1^1+…+p1^k1),通过这个式子不难想出sum
=sum[t]*sum[n/t],由动态规划的思想当处理到sum
时sum[n/t]和sum[t]已经被求出,所以这个式子可以在低于O(nlogn)的时间内处理出n以内所有数的因子和,当然此处还需要用到线性素数筛法提供每个数的最小素因数,注意当n=t时,显然用sum
=sum[t]*sum[n/t]=sum
不能求出sum
,此时再次用到上面那个因子和公式,用等比数列求和得到此时sum
=(mark
*n-1)/(mark
-1)(此处mark
表示n的最小素因数,mark数组在线性筛素数时可以得到)

Code

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
#define maxn 500005
int mark[maxn],prime[maxn],cnt;//prime存储素数,从0开始,mark[i]表示i的最小素因数
void Get_Prime(int n)//素数线性筛法
{
memset(mark,0,sizeof(mark));
cnt=0;
for(int i=2;i<=n;i++)
{
if(!mark[i])
mark[i]=prime[cnt++]=i;
for(int j=0;j<cnt&&prime[j]*i<=n;j++)
{
mark[i*prime[j]]=prime[j];
if(i%prime[j]==0)
break;
}
}
}
long long sumall[maxn];//sumall[i]表示i的所有因子和
void Get_all_sum(int n)//求出n以内每个数的因子和
{
sumall[1]=1;
for(int i=2;i<=n;i++)
{
int t=1;
for(int j=i;j%mark[i]==0;j/=mark[i])
t*=mark[i];
if(i==t)
sumall[i]=(1ll*mark[i]*i-1)/(mark[i]-1);
else
sumall[i]=sumall[t]*sumall[i/t];
}
}
int main()
{
Get_Prime(maxn);
Get_all_sum(maxn);
int T;
scanf("%d",&T);
while(T--)
{
int n;
scanf("%d",&n);
printf("%lld\n",sumall
-n);//此处因子不包括n自身
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: