PTA 要因要因

原題

階乗要因

問題の説明

正の整数 N が与えられると、N1、N2…NK は N の k 個の因子の集合体 (1 と N 自体を含む) であるため、N のすべての因子を効果的に計算できます。
この問題はやや複雑です。これらの k 因子に従って、これらの因子の因子の数をそれぞれ計算します。たとえば、N1 には n1 個の因子 (1 と N1 を含む) があり、N2 には n2 個の因子があり、…、NK には nk 個の因子があります。ここで問題が発生します。 S = n1 3 + n2 3 + …+ nk 3
の答えを見つけたいと思います

入力

入力の最初の行には 1 つの整数 T (T<=10000) があり、入力内のテスト ケースの数を示します。各テスト ケースには、正の整数 N (N < 2 31 )のみが存在します。

出力

テスト ケースごとに、答え S を 1 行出力します。

サンプル入力

2
6
9

サンプル出力

81
36

質問の意味分析:

質問の意味は、正の整数 N が与えられると、正の整数 N には k 個の因数 N1、N2、N3...Nk があり、N1 などの各 Ni には n1、n2、n3 などの k1 個の因数があります。この問題では、与えられた正の整数 N について、その各因数の因数の数の 3 次和を求めることが求められます。つまり、 n1 3 + n2 3 + …+ nk 3です。

一般的なアイデア:

1. 元のアイデア: 最初に総当たり法を使用しました。つまり、N のすべての因数の数を見つけた後、素因数分解(後で使用します)を使用して各因数に応じた因数の数を求めました。そして、3次加算を計算して和にします。これは明らかにタイムアウトします。後で、これに基づいた最適化方法、つまり、素数ふるいを見つけるというアイデアを使用して、すべての正の因子の数を見つけることができる因子ふるいを作成することを考えました。 n 内の整数を O(nlognlogn) 時間で計算します。ただし、この問題のデータ量は 2^ 31と大きいため、因子ふるいの一部しか作成できません。スペースを考慮せずに、因子ふるいを作成するだけです。231 には 1 分以上かかります。この考えは機能しません! 2. 正しい考え方は次のとおりです。 一生懸命考えているときに、あるアイデアが頭に浮かび、素因数分解によって因数の数を求める原理
について考え始めました。

素因数分解

正の整数 N の場合、N が素数でない場合、N は合成数です。N が合成数の場合、N は乗算された N 個の素数に分解されなければなりません。たとえば、12 は 2 つの 2 に 1 つの 3 を掛けたもの、つまり 12=2 2 3 1とみなすことができます。

素因数分解の因数を求める原理

上記の 12 を例として使用すると、12=2 2 3 1となります。この式を観察すると、主に 2 つの項 (2 と 3) があり、そのうち 2 つは 0、1、2 回 (3 つの可能性)、3 回出現する可能性があることがわかります。 0 と 1 の倍が出現する可能性があります (2 つの可能性)。乗算の原理によれば、2×3=6 個の因数の組み合わせが出現する可能性があります。つまり、 2 0 3 0 、2 0 3 1 2 1 3 02 1 3 1 2 2 3 0、 2 2 3 1、このことから、12 の因数の数は (2+1)*(1+1) = 6 に等しいと推測されます。

素因数を完成させた後に因数を求める原理を理解すれば、成功まであと一歩です。12 を例にとると、12 を 6 つの因数 2 0 3 0、 2 0 3 1、 2 1 3 0、 2 1 3 1、 2 2 3 0、 2 2 3 1に分解すると、次のことがわかりました。素因数分解形式から、(0+1) (0+1)=1、(0+1) (1+1) = 2、(1) であることが直接わかります。 +1)(0+1) = 2、(1+1)(1+1) = 4、(2+1)(0+1) = 3、(2+1)(1+ 1) = 6 因数、これらの結果の 3 乗和を加算すると、答えである 324 が見つかります。

ACコード

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <memory.h>
#include <cmath>
#include <ctime>
#include <cstdlib>
#define MAXN 10
typedef unsigned long long ll;
using namespace std;
int a[MAXN], na;
ll pow_mod(ll a,ll b,ll r)
{
    
    
    ll ans=1,buff=a;
    while(b)
    {
    
    
        if(b&1)
            ans=(ans*buff)%r;
        buff=(buff*buff)%r;
        b>>=1;
    }
    return ans;
}

bool test(ll n,ll a,ll d)
{
    
    
    if(n==2) return true;
    if(n==a) return false;
    if(!(n&1)) return false;
    while(!(d&1)) d>>=1;
    ll t = pow_mod(a,d,n);
    while(d!=n-1&&t!=n-1&&t!=1){
    
    
        t = t*t%n;//下面介绍防止溢出的办法,对应数据量为10^18次方;
        d<<=1;
    }
    return t == n-1||(d&1)==1;//要么t能变成n-1,要么一开始t就等于1
}

bool isPrime(ll n)
{
    
    
	if (n <= 1)		return false;
    int a[] = {
    
    2,3,5,7};//或者自己生成[2,N-1]以内的随机数rand()%(n-2)+2
    for(int i = 0; i <= 3; ++i){
    
    
        if(n==a[i]) return true;
        if(!test(n,a[i],n-1)) return false;
    }
    return true;
}

ll next_Prime(ll x){
    
    
	x++;
	if (x%2 == 0)	x++;
	while (!isPrime(x)) x += 2;	
	return x;
}
long long sum;
void f(int cur, int t){
    
    
	if (cur == na+1){
    
    
		sum += t*t*t;
		return ;
	}
	for (int i = 0; i <= a[cur]; i++){
    
    
		f(cur+1, t*(i+1));
	}
}
void solve(ll x){
    
    
	if (x == 1){
    
    
		sum = 1;
		return ;
	}else if (isPrime(x)){
    
    
		sum = 9;
		return ;
	}
	ll nowPrime = 2;
	sum = 0;
	na = 0;
	memset(a, 0, sizeof(a));
	while (x > 1 && (!isPrime(x))){
    
    
		if (x%nowPrime == 0){
    
    
			x /= nowPrime;
			a[na]++;
		}else{
    
    
		/*	do{
				nowPrime = next_Prime(nowPrime);
			}while (x%nowPrime);*/
			if (nowPrime == 2)		nowPrime++;
			else					nowPrime += 2;
			while (1){
    
    
				if (x%nowPrime == 0){
    
    
					if (isPrime(nowPrime)){
    
    
						break;
					}
				}
				nowPrime += 2;
			}
			na++;
		}
	}
	if (isPrime(x)){
    
    
		if (nowPrime == x){
    
    
			a[na]++;
		}else{
    
    
			a[++na]++;
		}
	}
	sort(a, a+na);
	f(0, 1);
}
int main(){
    
    	
	int N, i, j;
	ll x;
	scanf ("%d", &N);
	while (N--){
    
    
		scanf ("%lld", &x);
		solve(x);
		printf("%lld\n", sum);	
	}
	return 0;
}

上記のコードは素数を決定するために Miller Rabin アルゴリズムを使用しています. このアルゴリズムは素数を決定するための確率的アルゴリズムです. 一定の誤差の可能性があります. しかし、反復回数 k が 5 を超えると、誤差の可能性はほぼ 0 になりますであり、通常の O ( sqrt(n)) アルゴリズムと比較して、時間計算量 O(klog2(n)) が非常に優れています。この問題では、Miller Rabin アルゴリズム プログラムを使用すると 85 ミリ秒しかかかりませんが、通常のピクセル アルゴリズムを使用しても合格できますが、600 ミリ秒以上かかり、十分にエレガントではありません。
また、その考えは正しく、コードの素因数分解部分が激しすぎると、非常にタイムアウトしやすくなります。例えば、上記のACコードでは、コメント部分が素数かどうかを毎回判定する必要があります。これにより、多くのコンピューティング リソースが浪費されます (私はこの場所で長い間行き詰まっています)。コメントされていないコードを使用して、最初に nowPrime が割り切れるかどうかを確認し、割り切れる場合は、それが割り切れるかどうかを判断します。が素数の場合、状況ははるかに良くなります (当初、プログラムは 10,000 個のランダム データを処理するのに平均 2.5 秒かかりましたが、現在は 0.08 秒しかかかりません)

おすすめ

転載: blog.csdn.net/weixin_40735291/article/details/89341137