约数问题

版权声明: https://blog.csdn.net/weixin_39778570/article/details/82155300

首先,我们要先知道一个定理,任意一个自然数都可以分解为素数之积(素数不再分解)

这里写图片描述

  • 当我们对这条式子扩展到二维的时候:
    这里写图片描述

  • 当我们对这条式子扩展到n维的时候:
    这里写图片描述

那么其对应的自然N的n次方的约数个数为:

这里写图片描述

代码实现:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN = 1e6 + 10;

/*素数表*/
vector<int> ps;
bool is[40010];
void init() {
    fill(is, is + 40010, 1);
    ps.clear();
    is[0] = is[1] = false;
    for(int i = 2; i < 40010; ++i) {
        if(is[i]) {
            for(int j = i + i; j < 40010; j += i)is[j] = false;
        }
    }
    for(int i = 2; i < 40010; ++i) {
        if(is[i])ps.push_back(i);
    }
}
/* n的P次方的因数的个数 */
ll CC(ll n, ll p=1) {
    ll res = 1;
    /*ps[idx]*ps[idx]<=n  的设置原理 : 任何一个合数都可以分解为比这个合数小的素数之积*/
    /* 当ps大于分界线时 n/ps整除的结果 可能有两种情况:
        一种是素数(那么它必定小于分界线,所以在之前就被计算过了)
        另一种是合数(也小于分界线), 又合数可以分解为比它本身小的素数积,那么他也一定被计算过了*/
    for(int idx = 0; ps[idx]*ps[idx] <= n && idx < ps.size(); ++idx) {
        if(n % ps[idx] == 0) {
            int cnt = 0;
            while(n % ps[idx] == 0) {
                cnt++;
                n /= ps[idx];
            }
            res *= (1 + cnt * p);
        }
    }
//    printf("\n最后一个素数 %lld\n", n); 
    /* 相当于cnt等于1的情况,并且此时的n是一个素数 */
    /* 即最后一项为n^p n为最后一个素数*/
    if(n > 1)res *= (1 + 1 * p);
    return res;
}

相关题目:
http://acm.hdu.edu.cn/showproblem.php?pid=1492
给定一个特殊的数,这个数的约数只有2,3,5,7这几个素数,我们只需要将素数表设置为2,3,5,7就行

AC code

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN = 1e6 + 10;

vector<int> ps;
ll CC(ll n, ll p=1) {
    ll res = 1;
    /*ps[idx]*ps[idx]<=n  的设置原理 : 任何一个合数都可以分解为比这个合数小的素数之积*/
    /* 当ps大于分界线时 n/ps整除的结果 可能有两种情况:
        一种是素数(那么它必定小于分界线,所以在之前就被计算过了)
        另一种是合数(也小于分界线), 又合数可以分解为比它本身小的素数积,那么他也一定被计算过了*/
    for(int idx = 0; ps[idx]*ps[idx] <= n && idx < ps.size(); ++idx) {
        if(n % ps[idx] == 0) {
            int cnt = 0;
            while(n % ps[idx] == 0) {
                cnt++;
                n /= ps[idx];
            }
            res *= (1 + cnt * p);
        }
    }
//    printf("\n最后一个素数 %lld\n", n); 
    /* 相当于cnt等于1的情况,并且此时的n是一个素数 */
    if(n > 1)res *= (1 + 1 * p);
    return res;
}
int main() {
    ll n;
    ps.push_back(2);ps.push_back(3);ps.push_back(5);ps.push_back(7);
    while(scanf("%lld", &n) && n) printf("%lld\n",CC(n));
    return 0;
}

相关题目:
链接:https://www.nowcoder.com/acm/contest/90/F
来源:牛客网

题目描述
给定n,求1/x + 1/y = 1/n (x<=y)的解数。(x、y、n均为正整数)

输入描述:
在第一行输入一个正整数T。
接下来有T行,每行输入一个正整数n,请求出符合该方程要求的解数。
(1<=n<=1e9)
输出描述:
输出符合该方程要求的解数。

由1/x+1/y=1/n,可推得:x∗n+y∗n=x∗y,进一步可推得:x∗y−x∗n−y∗n+n^2=n^2,即:(x−n)(y−n)=n^2。

  题目要计算满足x≤y的解的个数。那么,从式子(x−n)(y−n)=n^2可以看出,(x−n)(y−n)相乘为n^2,即只要满足
  n^2 %(x−n)=0,n^2 %(y−n)=0就行。也就是,只要(x−n)和(y−n)为n^2的因子且x≤y就行了。
f(n)为算出n的正因子数,答案为两个因子之积且一个因子小于等于另一个因子
所求答案即为(f(n^2)+1)/2。加1的原因是因为x与y可以相等所以(x-n)与(y-n)也可以相等结果为n,所以n这个约数要算两次

AC code

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN = 1e6 + 10;

vector<int> ps;
bool is[40010];
void init() {
    fill(is, is + 40010, 1);
    ps.clear();
    is[0] = is[1] = false;
    for(int i = 2; i < 40010; ++i) {
        if(is[i]) {
            for(int j = i + i; j < 40010; j += i)is[j] = false;
        }
    }
    for(int i = 2; i < 40010; ++i) {
        if(is[i])ps.push_back(i);
    }
}
/* n的P次方的因数的个数 */

ll CC(ll n, ll p=1) {
    ll res = 1;
    /*ps[idx]*ps[idx]<=n  的设置原理 : 任何一个合数都可以分解为比这个合数小的素数之积*/
    /* 当ps大于分界线时 n/ps整除的结果 可能有两种情况:
        一种是素数(那么它必定小于分界线,所以在之前就被计算过了)
        另一种是合数(也小于分界线), 又合数可以分解为比它本身小的素数积,那么他也一定被计算过了*/
    for(int idx = 0; ps[idx]*ps[idx] <= n && idx < ps.size(); ++idx) {
        if(n % ps[idx] == 0) {
            int cnt = 0;
            while(n % ps[idx] == 0) {
                cnt++;
                n /= ps[idx];
            }
            res *= (1 + cnt * p);
        }
    }
//    printf("\n最后一个素数 %lld\n", n); 
    /* 相当于cnt等于1的情况,并且此时的n是一个素数 */
    if(n > 1)res *= (1 + 1 * p);
    return res;
}
int main() {
    /*init();*/
    ll n;
    int T;

    for(scanf("%d", &T); T--;) {
        scanf("%lld", &n);
        // +1 是因为n在2次方中只算了一个约数而结果x<=y 可以取同一个结果2n, 又n^2有两个,更高阶依次类推 
        printf("%lld\n", (CC(n,2) + 1) / 2);
    }
    return 0;
}

另外,我们来看一下所有约数和问题

约数的和是在严格分解质因数后,将M的每个质因数最高次幂的所有约数的和相乘所得到的积.
如:21000=2^3×3×5^3×7,所以21000所有约数的和为(1+2+2^2+2^3)×(1+3)×(1+5+5^2+5^3)×(1+7)=74880.
360 = 2^3×3^2×5 约数和为:(1+3+3^2)×(1+2+2^2+2^3)×(1+5) = 1170
- 代码实现如下

#include<bits/stdc++.h>
#define FOR(i,j,n) for(int i=j; i<n; ++i)
#define P pair<int, int>
#define ll long long
using namespace std;

vector<int> ps;
bool is[40010];
void init(){
    fill(is, is+40010, 1);
    ps.clear();
    is[0] = is[1] = false;
    FOR(i,2,40010){
        if(is[i]){
            ps.push_back(i);
            for(int j=i+i; j<40010; j+=i) is[i] = false;
        }
    }
}
vector<P> qin; // 放入因子和次数
ll CC(ll n, ll p=1){
    qin.clear();
    ll res = 1;
    for(int idx=0; ps[idx]*ps[idx]<=n && idx<ps.size(); ++idx){
        if(n % ps[idx] == 0){
            int cnt = 0;
            while(n%ps[idx]==0){
                cnt++;
                n /= ps[idx];
            }
            res *= (1 + cnt*p);
            qin.push_back(P(ps[idx],cnt));
        }
    }
    if(n > 1) res *= (1 + 1*p);
    if(n > 1) qin.push_back(P(n,1));
    return res;
}
ll C_sum(ll a){
    CC(a);
    ll res = 1;
    for(int x=0; x<qin.size(); x++){
        int cnt = qin[x].second;
        int num = qin[x].first;
        ll p = 1;
        ll now = 1;
        FOR(i,1,cnt+1){
            p *= num;
            now += p;
        }
        res *= now;
        //cout << now << endl;
    }
    return res;
}
int main(){
    init();
    int t, a, b;
    cout<< C_sum(360) << endl;
    cout<< C_sum(21000) << endl;

    return 0;
} 

参考:http://www.cnblogs.com/565261641-fzh/p/8641852.html

猜你喜欢

转载自blog.csdn.net/weixin_39778570/article/details/82155300