【19南昌网络赛】A-PERFECT NUMBER PROBLEM

这篇博客介绍了博主在19南昌网络赛中遇到的‘完美数’问题,遵循小学课本上的定义。博主分享了自己的解题思路,包括Euler筛选素数、利用唯一分解定理和等比数列求和公式解决问题,同时也提到WSY的简洁解法,耗时稍长。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

参考博客:WSY大佬的做法

【题解】:

其实看到题目我就知道这个结论了,这个是我们小学课本上面的一个“完美数”。

没有规律可言。(当时比赛时候我是直接上网找到,其实不太道德,主要是看到太多人在10分钟内过了)

主要是看了WSY对于这个题目的执着,我自己也写了一遍。


【基本思想】

1、Euler把所有素数筛选出来

2、用唯一分解定理把所有的 质因数 与其  对应的 个数挑出来。

3、利用公式推出:

(1+p_{1}^1+p_{1}^2+\cdots+p_{1}^{k1})*(1+p_{2}^1+p_{2}^2+\cdots+p_{2}^{k2})* \cdots* (1+p_{n}^1+p_{n}^2+\cdots+p_{n}^{kn})

利用等比数列求和得到:

(\frac{1-p_{1}^{k_{1}+1}}{1-p_{1}})* (\frac{1-p_{2}^{k_{2}+1}}{1-p_{2}})*\cdots*(\frac{1-p_{n}^{k_{n}+1}}{1-p_{n}})


#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 5e5;
const int M = 1e5;
bitset <N> is_prime;
int prime[M],p_cnt;
ll qpow(ll a,ll b){
    ll ans = 1 ;
    while( b ){
        if( b&1 ) ans = ans *a ;
        a = a * a ;
        b>>=1 ;
    }
    return ans ;
}
void Euler(){
    is_prime.set();
    for(ll i=2;i<N;i++){
        if( is_prime[i] ){
            prime[p_cnt++] = i;
        }
        for(ll j=0;j<p_cnt && i*prime[j]<N ;j++){
            is_prime[ i*prime[j] ] = 0;
            if( i%prime[j] == 0 ){
                break;
            }
        }
    }
}
ll P[100],K[100];
int main()
{
    Euler();
    for(int i=0;i<10;i++){
        printf("%d\n",prime[i]);
    }
    int cnt = 0 ;
    for(ll i=2;cnt<5;i++){
        memset(P,0,sizeof(P));
        memset(K,0,sizeof(K));
        ll x = i,tx = i;
        int tot = 0 ;
        for( int j=0; j<p_cnt && tx >= prime[j]*prime[j] ; j++){
            if( tx % prime[j] == 0 ) {
                P[tot] = prime[j];
                while( tx%prime[j]==0 ) {
                    K[tot]++;
                    tx/=prime[j];
                }
                tot++;
            }
        }
        if( tx != 1 ){
            P[tot] = tx;
            K[tot] ++ ;
            tot++;
        }
        ll tmp = 1;
        for(int j=0;j<tot;j++){
            tmp = tmp * (qpow(P[j],K[j]+1)-1)/(P[j]-1) ;
        }
        /*if( i==6 ){
            for(int j=0;j<tot;j++){
                printf("( %lld , %lld )\n",P[j],K[j]);
            }
            printf("Tmp :%lld\n",tmp);
        }*/
        if (tmp == x<<1 ){
            printf("%lld\n",x);
            cnt++;
        }
    }
    return 0;
}

我的方法主要是用到了推导式:耗时66s

但是SY兄的写法更加简洁:耗时73s

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5+7;
bool vis[maxn];
int prim[maxn],cnt;
void sprim(){
    for(int i=2;i<maxn;i++){
        if(!vis[i])prim[cnt++] = i;
        for(int j=0;j<cnt && prim[j]*i<maxn;j++){
            vis[prim[j]*i] = 1;
            if(i%prim[j] == 0)break;
        }
    }
}
int main(){
    sprim();
    //for(int i=0;i<20;i++)printf("%d\n",prim[i]);
	for(ll i=1;;i++){
        ll x = 1,ti = i;
        for(int j=0;j<cnt && 1ll*prim[j]*prim[j]<=ti;j++){
            if(ti%prim[j] == 0){
                ll p = prim[j];
                ll tmp = 1;
                while(ti%prim[j] == 0)
                    tmp += p,p *= prim[j],ti /= prim[j];
                x *= tmp;
            }
        }
        if(ti != 1)
            x *= (1+ti);
        if(x == 2*i)printf("%lld\n",i);
	}
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值