HDU 1215 七夕节(因子和)

201 篇文章 10 订阅

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[n]=sum[t]*sum[n/t],由动态规划的思想当处理到sum[n]时sum[n/t]和sum[t]已经被求出,所以这个式子可以在低于O(nlogn)的时间内处理出n以内所有数的因子和,当然此处还需要用到线性素数筛法提供每个数的最小素因数,注意当n=t时,显然用sum[n]=sum[t]*sum[n/t]=sum[n]不能求出sum[n],此时再次用到上面那个因子和公式,用等比数列求和得到此时sum[n]=(mark[n]*n-1)/(mark[n]-1)(此处mark[n]表示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);//此处因子不包括n自身 
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值