数学知识——约数个数

约数

如果一个数 a a a除以另一个数 b b b的余数为0,即 a % b a\%b a%b == 0, 则 b b b a a a的约数。

试除法判断约数

时间复杂度 O ( n ) O(\sqrt n) O(n )
一个数的约数是成对出现的,并且一个在 n \sqrt n n 左边,一个在右边,所以只需要枚举到 n \sqrt n n 即可。

void get_divisors(int x)
{
    vector<int> res;
    for (int i = 1; i <= x / i; i ++ )
    {
        if (x % i == 0)
        {
            res.push_back(i);
            if (i != x / i) res.push_back(x / i);
        }
    }
    sort(res.begin(), res.end());
    for (auto item : res) cout << item << " ";
    cout << endl;
    return ;
}

约数个数

有一个数 N N N,可以进行质因数分解: N = p 1 α 1 p 2 α 2 p 3 α 3 . . . p k α k N=p_1^{\alpha_1}p_2^{\alpha_2}p_3^{\alpha_3}...p_k^{\alpha_k} N=p1α1p2α2p3α3...pkαk。任何一个约数可以表示成 d = p 1 β 1 p 2 β 2 p 3 β 3 . . . p k β k β i ≤ α i d=p_1^{\beta_1}p_2^{\beta_2}p_3^{\beta_3}...p_k^{\beta_k}\beta_i \leq \alpha_i d=p1β1p2β2p3β3...pkβkβiαi β 1 \beta_1 β1 α 1 + 1 \alpha_1 + 1 α1+1种选法, β 2 \beta_2 β2 α 2 + 1 \alpha_2 + 1 α2+1种选法, β k \beta_k βk α k + 1 \alpha_k+ 1 αk+1种选法,所以约数个数一共有 ( α 1 + 1 ) ( α 2 + 1 ) ( α 3 + 1 ) . . . ( α k + 1 ) (\alpha_1 + 1)(\alpha_2 + 1)(\alpha_3 + 1)...(\alpha_k + 1) (α1+1)(α2+1)(α3+1)...(αk+1)种。

unordered_map<int, int> primes;
while(n--){
        cin >> x;
        for(int i = 2;i <= x/i; ++i){
            while(x % i == 0){
                x /= i;
                primes[i] ++;
            }
        }
        if(x > 1) primes[x] ++;
    }
    for(auto i : primes) ans = ans*(i.second + 1) % mod;
    cout << ans;

约数之和

有一个数 N N N,可以进行质因数分解: N = p 1 α 1 p 2 α 2 p 3 α 3 . . . p k α k N=p_1^{\alpha_1}p_2^{\alpha_2}p_3^{\alpha_3}...p_k^{\alpha_k} N=p1α1p2α2p3α3...pkαk。任何一个约数可以表示成 d = p 1 β 1 p 2 β 2 p 3 β 3 . . . p k β k β i ≤ α i d=p_1^{\beta_1}p_2^{\beta_2}p_3^{\beta_3}...p_k^{\beta_k}\beta_i \leq \alpha_i d=p1β1p2β2p3β3...pkβkβiαi。约数之和为 ( 1 + p 1 1 + p 1 2 + . . . + p 1 α 1 ) ( 1 + p 2 1 + p 2 2 + . . . + p 2 α 2 ) . . . ( 1 + p k 1 + p k 2 + . . . + p k α k ) (1+p_1^1+p_1^2+...+p_1^{\alpha_1})(1+p_2^1+p_2^2+...+p_2^{\alpha_2})...(1+p_k^1+p_k^2+...+p_k^{\alpha_k}) (1+p11+p12+...+p1α1)(1+p21+p22+...+p2α2)...(1+pk1+pk2+...+pkαk)

unordered_map<int, int> primes;

    while (n -- )
    {
        int x;
        cin >> x;

        for (int i = 2; i <= x / i; i ++ )
            while (x % i == 0)
            {
                x /= i;
                primes[i] ++ ;
            }

        if (x > 1) primes[x] ++ ;
    }

    LL res = 1;
    for (auto p : primes)
    {
        LL a = p.first, b = p.second;
        LL t = 1;
        while (b -- ) t = (t * a + 1) % mod;
        res = res * t % mod;
    }

    cout << res << endl;

最大公约数

求两个正整数 a a a b b b 的 最大公约数 d d d
则有 g c d ( a , b ) = g c d ( b , a % b ) gcd(a,b) = gcd(b,a\%b) gcd(a,b)=gcd(b,a%b)
证明:
a % b = a − k ∗ b a\%b = a - k*b a%b=akb 其中 k = a / b k = a/b k=a/b(向下取整)
d d d ( a , b ) (a,b) (a,b)的公约数 则知 d ∣ a d|a da d ∣ b d|b db 则易知 d ∣ a − k ∗ b d|a-k*b dakb d d d也是 ( b , a % b ) (b,a\%b) (b,a%b) 的公约数
d d d ( b , a % b ) (b,a\%b) (b,a%b)的公约数 则知 d ∣ b d|b db d ∣ a − k ∗ b d|a-k*b dakb d ∣ a − k ∗ b + k ∗ b = d ∣ a d|a-k*b+k*b = d|a dakb+kb=da 故而 d d d同时整除 a a a b b b 所以 d d d也是 ( a , b ) (a,b) (a,b)的公约数
因此 ( a , b ) (a,b) (a,b)的公约数集合和 ( b , a % b ) (b,a\%b) (b,a%b)的公约数集合相同 所以他们的最大公约数也相同
时间复杂度 O ( l o g ( a + b ) ) O(log(a+b)) O(log(a+b))

int gcd(int a, int b)
{
    return b ? gcd(b, a % b) : a;
}

轻拍牛头

今天是贝茜的生日,为了庆祝自己的生日,贝茜邀你来玩一个游戏.

贝茜让 N N N 头奶牛(编号 1 1 1 N N N)坐成一个圈。除了 1 1 1 号与 N N N 号奶牛外,i 号奶牛与 i − 1 i−1 i1 号和 i + 1 i+1 i+1 号奶牛相邻, N N N 号奶牛与 1 1 1 号奶牛相邻。

农夫约翰用很多纸条装满了一个桶,每一张纸条中包含一个 到 1000000 之间的数字。

接着每一头奶牛 i i i 从桶中取出一张纸条,纸条上的数字用 A i A_i Ai 表示。

所有奶牛都选取完毕后,每头奶牛轮流走上一圈,当走到一头奶牛身旁时,如果自己手中的数字能够被该奶牛手中的数字整除,则拍打该牛的头。

牛们希望你帮助他们确定,每一头奶牛需要拍打的牛的数量。

即共有 N N N 个整数 A 1 , A 2 , … , A N A_1,A_2,…,A_N A1,A2,,AN,对于每一个数 A i A_i Ai,求其他的数中有多少个是它的约数。

输入格式
第一行包含整数 N N N

接下来 N N N 行,每行包含一个整数 A i A_i Ai

输出格式
N N N 行,第 i i i 行的数字为第 i i i 头牛需要拍打的牛的数量。

数据范围
1 ≤ N ≤ 1 0 5 , 1≤N≤10^5, 1N105,
1 ≤ A i ≤ 1 0 6 1≤Ai≤10^6 1Ai106

输入样例:

5
2
1
2
3
4

输出样例:

2
0
2
1
3

如果直接采用暴力的做法,时间复杂度为 O ( N 2 ) ( N 为数据规模 ) O(N^2)(N为数据规模) O(N2)(N为数据规模)
考虑对于每一个数,事先存储下来,并且,并且枚举他们的倍数,可以把时间复杂度降到 O ( M + M / 2 + M / 3 + . . . + M / N ) = O ( M l o g N ) ( M 为 A i 上限 ) O(M+M/2+M/3+...+M/N)=O(MlogN)(M为A_i上限) O(M+M/2+M/3+...+M/N)=O(MlogN)(MAi上限)

#include 
using namespace std;
const int N = 1000010;
int a[N], sum[N], cnt[N];
int main()
{
    int n;
    cin >> n;
    for (int i = 1; i <= n; i ++ )
    {
        cin >> a[i];
        cnt[a[i]] ++ ;
    }
    for (int i = 1; i < N; i ++ )
    {
        if (!cnt[i]) continue;
        for (int j = i; j < N; j += i )
            sum[j] += cnt[i];
    }
    for (int i = 1; i <= n; i ++ )
        cout << sum[a[i]] - 1 << endl;
    return 0;
}

樱花

求方程:

1 x + 1 y = 1 n ! \dfrac{1}{x} + \dfrac{1}{y} = \dfrac{1}{n!} x1+y1=n!1

的正整数解的组数,答案对 1 0 9 + 7 10^9+7 109+7 取模。

输入格式

输入只有一行一个整数,表示 n n n

输出格式

输出一行一个整数表示正整数解的组数模 1 0 9 + 7 10^9+7 109+7 的值。

输入输出样例 #1

输入 #1

2

输出 #1

3

输入输出样例 #2

输入 #2

1439

输出 #2

102426508

说明/提示

样例 1 解释

共有三个数对 ( x , y ) (x,y) (x,y) 满足条件,分别是 ( 3 , 6 ) , ( 4 , 4 ) (3,6),(4,4) (3,6),(4,4) ( 6 , 3 ) (6,3) (6,3)

数据规模与约定

  • 对于 30 % 30\% 30% 的数据,保证 n ≤ 100 n\le 100 n100
  • 对于 100 % 100\% 100% 的数据,保证 1 ≤ n ≤ 1 0 6 1 \le n\le 10^6 1n106

1 x + 1 y = 1 n ! \dfrac{1}{x} + \dfrac{1}{y} = \dfrac{1}{n!} x1+y1=n!1

( x + y ) n ! = x y (x+y)n!=xy (x+y)n!=xy

x = y n ! y − n ! = ( y − n ! + n ! ) n ! y − n ! = n ! + ( n ! ) 2 y − n ! x=\dfrac{yn!}{y-n!}=\dfrac{(y-n!+n!)n!}{y-n!}=n!+\dfrac{(n!)^2}{y-n!} x=yn!yn!=yn!(yn!+n!)n!=n!+yn!(n!)2

同时又因为 y > n ! y>n! y>n!,所以 y − n ! y-n! yn!一定 > 0 >0 >0,本问题可以转换为求 ( n ! ) 2 (n!)^2 (n!)2的约数个数。

#include 
using namespace std;
const int N = 1000010;
const int mod = 1e9 + 7;
typedef long long ll;
int prime[N];
bool st[N];
int n, cnt = 0;
void init(int n)
{
    for (int i = 2; i <= n; i ++ )
    {
        if (!st[i]) prime[cnt ++ ] = i;
        for (ll j = 0; prime[j] * i <= n; j ++ )
        {
            st[i * prime[j]] = 1;
            if (i % prime[j] == 0)break;
        }
    }
}
int main()
{
    cin >> n;
    ll res = 1;
    init(n);
    for(int i = 0; i < cnt; i ++ )
    {
        int p = prime[i];
        ll s = 0;
        for (ll j = p; j <= n; j *= p) s += n / j;
        res = res * (2 * s + 1) % mod;
    }
    cout << res;
    return 0;
}

反素数

对于任何正整数 x x x,其约数的个数记作 g ( x ) g(x) g(x)。例如 g ( 1 ) = 1 g(1)=1 g(1)=1 g ( 6 ) = 4 g(6)=4 g(6)=4

如果某个正整数 x x x 满足: ∀ 0 < i < x \forall 0 \lt i \lt x ∀0<i<x,都有 g ( x ) > g ( i ) g(x) \gt g(i) g(x)>g(i),则称 x x x反质数。例如,整数 1 , 2 , 4 , 6 1,2,4,6 1,2,4,6 等都是反质数。

现在给定一个数 N N N,你能求出不超过 N N N 的最大的反质数么?

输入格式

一个数 N N N

输出格式

不超过 N N N 的最大的反质数。

输入输出样例 #1

输入 #1

1000

输出 #1

840

说明/提示
1 ≤ N ≤ 2 × 1 0 9 1 \leq N \leq 2 \times 10^9 1N2×109

三个关键点:
1.对于反素数分解质因数,一定满足其中大的质数的次数一定不大于前面小的次数的质数,因为如果后者次数大,我们就可以交换前后两者的次数,这样结果不变,但是会得到更小的数。
2.分解质因数后的质数只会是 2 , 3 , 5 , 7 , 11 , 13 , 17 , 19 , 21 , 23 2,3,5,7,11,13,17,19,21,23 2,3,5,7,11,13,17,19,21,23中的数,因为如果再多一个质数,乘积就大于 N N N了。
3.次数之和一定不超过30,因为 2 31 > 2 ∗ 1 0 9 2^{31}>2*10^9 231>2109

根据以上性质进行深搜。

#include 
using namespace std;
typedef long long ll;
const int N = 2e9 + 10;
int primes[] = {2, 3, 5, 7, 11, 13, 17, 19, 23};
int maxd, number;
int n;
void dfs(int u, int last, int p, int s)
{
    if (s > maxd || (s == maxd && p < number))
    {
        maxd = s;
        number = p;
    }
    if (u == 9) return ;
    for (int i = 1; i <= last; i ++ )
    {
        if ((ll) p * primes[u] > n) break;
        p *= primes[u];
        dfs(u + 1, i, p, s * (i + 1));
    }
    return ;
}
int main()
{
    cin >> n;
    // 枚举到哪一个素数,当前素数最大取几个,当前数,当前约数个数
    dfs(0, 30, 1, 1);
    cout << number << endl;
    return 0;
}

Hankson 的趣味题

Hanks 博士是 BT(Bio-Tech,生物技术) 领域的知名专家,他的儿子名叫 Hankson。现在,刚刚放学回家的 Hankson 正在思考一个有趣的问题。

今天在课堂上,老师讲解了如何求两个正整数 c 1 c_1 c1 c 2 c_2 c2 的最大公约数和最小公倍数。现在 Hankson 认为自己已经熟练地掌握了这些知识,他开始思考一个“求公约数”和“求公倍数”之类问题的“逆问题”,这个问题是这样的:已知正整数 a 0 , a 1 , b 0 , b 1 a_0,a_1,b_0,b_1 a0,a1,b0,b1,设某未知正整数 x x x 满足:

  1. x x x a 0 a_0 a0 的最大公约数是 a 1 a_1 a1

  2. x x x b 0 b_0 b0 的最小公倍数是 b 1 b_1 b1

Hankson 的“逆问题”就是求出满足条件的正整数 x x x。但稍加思索之后,他发现这样的 x x x 并不唯一,甚至可能不存在。因此他转而开始考虑如何求解满足条件的 x x x 的个数。请你帮助他编程求解这个问题。

输入格式

第一行为一个正整数 n n n,表示有 n n n 组输入数据。接下来的$ n$ 行每行一组输入数据,为四个正整数 a 0 , a 1 , b 0 , b 1 a_0,a_1,b_0,b_1 a0,a1,b0,b1,每两个整数之间用一个空格隔开。输入数据保证 a 0 a_0 a0 能被 a 1 a_1 a1 整除, b 1 b_1 b1 能被 b 0 b_0 b0 整除。

输出格式

n n n 行。每组输入数据的输出结果占一行,为一个整数。

对于每组数据:若不存在这样的 x x x,请输出 0 0 0,若存在这样的 x x x,请输出满足条件的 x x x 的个数;

输入输出样例 #1

输入 #1

2 
41 1 96 288 
95 1 37 1776

输出 #1

6 
2

说明/提示

【样例解释】

第一组输入数据, x x x 可以是 9 , 18 , 36 , 72 , 144 , 288 9,18,36,72,144,288 9,18,36,72,144,288,共有 6 6 6 个。

第二组输入数据, x x x 可以是 48 , 1776 48,1776 48,1776,共有 2 2 2 个。

【数据范围】

  • 对于 50 % 50\% 50% 的数据,保证有 1 ≤ a 0 , a 1 , b 0 , b 1 ≤ 10000 1\leq a_0,a_1,b_0,b_1 \leq 10000 1a0,a1,b0,b110000 n ≤ 100 n \leq 100 n100
  • 对于 100 % 100\% 100% 的数据,保证有 1 ≤ a 0 , a 1 , b 0 , b 1 ≤ 2 × 1 0 9 1 \leq a_0,a_1,b_0,b_1 \leq 2 \times 10^9 1a0,a1,b0,b12×109 n ≤ 2000 n≤2000 n2000

如果采用暴力方法,即枚举 b 1 b_1 b1的每个约数,再判断这个约数是否满足条件的话,时间复杂度为 O ( T b 1 l o g b 1 ) O(T\sqrt{b_1}logb_1) O(Tb1 logb1)
考虑先求出 N N N以内的所有素数,对于每个 b 1 b_1 b1,分解质因数,dfs得到所有约数,再对于每个约数判断是否合法。
时间复杂度:
1.预处理所有的约数 O ( N ) O(N) O(N)
2.分解质因数,因为已经得到了所有的质数,所以枚举质数即可,不需要枚举到 b 1 \sqrt{b_1} b1 ,因此时间复杂度为 O ( T ∗ b 1 / l n b 1 ) O(T*\sqrt{b_1}/ln\sqrt{b_1}) O(Tb1 /lnb1 )
3.平均每个数约数的个数为 l o g b 1 logb_1 logb1,做判断的时间复杂度为 l o g b 1 logb_1 logb1,因此对于每个约数判断是否合法的时间复杂度总和为 O ( T ∗ ( l o g b 1 ) 2 ) ) O(T*(logb_1)^2)) O(T(logb1)2))

#include 
#include 
#include 
using namespace std;
#define N 500010
struct Data{
    int p, c;
}factor[N];
int prime[N];
bool st[N];
int divider[N];
typedef long long ll;
//cnt 记录素数个数,cnt2记录一个数可以分解为的不同质因子个数
//cnt3记录一个数的约数个数
int n, cnt = 0, cnt2 = 0, cnt3 = 0;
int a0, a1, b0, b1;
void init(int n)
{
    for (int i = 2; i <= n; i ++ )
    {
        if (!st[i])prime[cnt ++ ] = i;
        for (int j = 0; prime[j] * i <= n; j ++ )
        {
            st[i * prime[j]] = 1;
            if (i % prime[j] == 0)break;
        }
    }
}
void divide(int x)
{
    cnt2 = 0;
    for (int i = 0; prime[i] <= sqrt(x); i ++ )
    {
        int p = prime[i];
        if (x % p == 0)
        {
            int s = 0;
            while (x % p == 0)
            {
                s += 1;
                x /= p;
            }
            factor[cnt2 ++ ] = {p, s};
        }
    }
    if(x > 1) factor[cnt2 ++ ] ={x, 1};
}
void dfs(int u, int p)
{
    if (u == cnt2)
    {
        divider[cnt3 ++ ] = p;
        return ;
    }
    for (int i = 0; i <= factor[u].c; i ++ )
    {
        dfs(u + 1, p);
        p *= factor[u].p;
    }
    return ;
}
int gcd(int x, int y)
{
    return y ? gcd(y, x % y) : x;
}
ll lcm(int x, int y)
{
    return (ll) x * y / gcd(x, y);
}
int main()
{
    init(N - 1);
    cin >> n;
    while(n -- )
    {
        cin >> a0 >> a1 >> b0 >> b1;
        divide(b1);
        cnt3 = 0;
        dfs(0, 1);
        int res = 0;
        for (int i = 0; i < cnt3; i ++ )
        {
            if(gcd(a0, divider[i]) == a1 && lcm(b0, divider[i]) == b1)
            res ++ ;
        }
        cout << res << endl;
    }
    return 0;
}

你可能感兴趣的:(深度优先,算法,图论)