数论基础知识总结

最大公约数

欧几里得算法: 两个整数的最大公约数等于其中较小的那个数和两数相除余数的最大公约数。
表达式: g c d ( a , b ) = g c d ( b , a % b ) , a ≥ b & b ≠ 0 gcd(a,b)=gcd(b,a \%b),a\geq b \& b \neq 0 gcd(a,b)=gcd(b,a%b),ab&b=0
C++库函数提供求最大公约数的函数 _ _ g c d ( a , b ) gcd(a,b) gcd(a,b)求a和b的最大公约数。

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

最小公倍数

l c m ( a , b ) = a ∗ b g c d ( a , b ) lcm(a,b) = \frac{a*b}{gcd(a,b)} lcm(a,b)=gcd(a,b)ab

long long lcm(long long a,long long b)
{
    
    
    return a*b/__gcd(a,b);
}

又见GCD
题目大意
有三个正整数 a , b , c ( 0 < a , b , c < 1 0 6 ) a,b,c(0<a,b,c<10^6) a,b,c(0<a,b,c<106),其中 c c c 不等于 b b b。若 a a a c c c 的最大公约数为 b b b,现已知 a a a b b b,求满足条件的最小的 c c c
输入格式
第一行输入一个 n n n,表示有 n n n 组测试数据,接下来的 n n n 行,每行输入两个正整数 a a a , b b b
输出格式
输出对应的 c c c,每组测试数据占一行。
输入样例

2
6 2
12 4

输出样例

4
8

水题,枚举求最大公约数, a a a c c c 的最大公约数为 b b b,给出了 a a a b b b,直接在 ( a , b ) (a,b) a,b的范围中枚举是否有 g c d ( a , c ) = = b gcd(a,c) == b gcd(a,c)==b

#include <cstdio>
#include <algorithm>
using namespace std;
int main()
{
    
    
    int n;
    scanf("%d",&n);
    while(n--)
    {
    
    
        int x,y;
        scanf(" %d%d",&x,&y);
        for(int z = y*2; z <= x; z +=y)
            if(__gcd(x,z) == y)
            {
    
    
                printf("%d\n",z);
                break;
            }
    }
    return 0;
}
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;
ll gcd(ll a, ll b)
{
    
    
    return b ? gcd(b, a%b) : a;
}
int main()
{
    
    
    int n;
    scanf("%d",&n);
    while(n--)
    {
    
    
        ll x,y;
        scanf(" %lld%lld",&x,&y);
        for(ll z = y*2; z <= x; z +=y)
            if(gcd(x,z) == y)
            {
    
    
                printf("%lld\n",z);
                break;
            }
    }
    return 0;
}

Least Common Multiple
题目大意
给出一组数,求这组数的最小公倍数是多少?
输入格式
输入一个 T T T 表示有 T T T 组数据。
每组的第一个数 n n n 代表这组数据有 n n n个数。
输出格式
输出每组数据的最小公倍数,每组数据占一行。
输入样例

2
3 5 7 15
6 4 10296 936 1287 792 1

输出样例

105
10296
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;
ll gcd(ll a, ll b)
{
    
    
    return b ? gcd(b, a%b) : a;
}
int main()
{
    
    
    int n;
    scanf("%d",&n);
    while(n--)
    {
    
    
        int t;
        ll x,y,res = 0;
        scanf(" %d %lld",&t,&x);
        res = x;
        while(--t)
        {
    
    
            scanf("%lld",&y);
            ll z = gcd(res, y);
            res = res*y/z;
        }
        printf("%lld\n",res);
    }
    return 0;
}

最小公倍数
题目大意
给定两个正整数,计算这两个数的最小公倍数。
输入格式
输入包含多组测试数据,每组只有一行,包括两个不大于1000的正整数.
输出格式
对于每个测试用例,给出这两个数的最小公倍数,每个实例输出一行。

#include <cstdio>
#include <algorithm>
using namespace std;
int main()
{
    
    
    int x,y;
    while(~scanf(" %d %d",&x,&y))
        printf("%d\n",x*y/__gcd(x,y));
    return 0;
}

素数

素数(质数) : 除了1和它本身外,不能被其余自然数整除的数。

扫描二维码关注公众号,回复: 12140091 查看本文章

筛素数
朴素筛法求素数

bool st[maxn];  //存储的素数是否被筛除,true表示筛除,false表示未筛除
int primes[maxn], cnt;  //存储素数
void get_primes(int n)
{
    
    
    memset(st,0,sizeof st); cnt = 0;
    for(int i = 2; i <= n; i++)
    {
    
    
        if(st[i]) continue;
        primes[cnt++] = i;
        for(int j = i; j <= n; j += i) st[j] = true;
    }
}

埃式筛法: 以18为例举个例子,咋们筛选13以内的所有素数。 T T T 表示是素数, F F F 表示不是素数。

最初全部初始化成为T

2 3 4 5 6 7 8 9 10 11 12 13
T T T T T T T T T T T T

第一轮筛选,最小的数是 2,则 2 是素数,筛除所有 2 的倍数。

2 3 4 5 6 7 8 9 10 11 12 13
T T F T F T F T F T F T

第二轮筛选,最小的数是 3,则 3 是素数,筛除所有 3 的倍数。

2 3 4 5 6 7 8 9 10 11 12 13
T T F T F T F F F T F T

第三轮筛选,最小的数是 5,则 5 是素数,筛除所有 5 的倍数。

2 3 4 5 6 7 8 9 10 11 12 13
T T F T F T F F F T F T

第四轮筛选,最小的数是 7,则 7 是素数,筛除所有 7 的倍数。

2 3 4 5 6 7 8 9 10 11 12 13
T T F T F T F F F T F T

第五轮筛选,最小的数是 11,则 11 是素数,筛除所有 11 的倍数。

2 3 4 5 6 7 8 9 10 11 12 13
T T F T F T F F F T F T

第六轮筛选,最小的数是 13,则 13 是素数,筛除所有 13 的倍数。

2 3 4 5 6 7 8 9 10 11 12 13
T T F T F T F F F T F T

筛完最终的素数是:2,3,5,7,11。

从上面的筛法看,一个数会被筛除多次,我们要使得筛法的时间复杂度是线性的做法,那么每个数只能被筛除一次。
快速线性筛法是外层循环枚举倍数,内层循环枚举素数,然后通过 “素数 × \times × 倍数 = 合数”来筛素数。因为采用最小的那个素因子开始,只要保证了枚举的素数是合数的最小素因子。假设倍数没有比素数更小的素因子,合数的最小素因子就是素数。

时间复杂度 O ( n l o g l o g n ) O(nlog{logn}) O(nloglogn)

/***
对于一个合数 x,假设 primes[j] 是 x最小的质因子
当 i 枚举到 x/primes[j] 的时候筛除
***/
int primes[maxn], cnt;  //primes[]存储所有的素数
bool st[maxn];  //st[x] 存储的 x 是否被筛除
void get_primes(int n)
{
    
    
    for(int i = 2; i <= n; i++)
    {
    
    
        if(!st[i]) primes[cnt++] = i;
        for(int j = 0; primes[j] <= n/i; j++)
        {
    
    
            st[primes[j]*i] = true;
            /*** 
            i%primes[j] == 0说明 primes[j] 是 i 的最小质因子,因为素因子枚举是
            从小到大的,在不断循环后倍数 i 就有了比 primes[j]更小的素因子。
            得出结论:i%primes[j] !=0 ,primes[j]一定小于 i 的所有质因子
            i%primes[j] == 0 ,primes[j] 一定是 primes[j]*i的最小质因子。
            ***/
            if(i%primes[j] == 0) break;
        }
    }
}

分拆素数和
题目大意
把一个偶数拆成两个不同素数的和,有几种拆法呢?
输入格式
输入包含一些正的偶数,其值不会超过10000,个数不会超过500,若遇0,则结束。
输入格式
对应每个偶数,输出其拆成不同素数的个数,每个结果占一行。
输入样例

30
26
0

输出样例

3
2

解题思路就是做好预处理,把1 e 4 e^4 e4 以内的素数筛出来,然后进行枚举,枚举到这个数一半的时候结束。

#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn = 1e4 + 7;
int primes[maxn], cnt;  //primes[]存储所有的素数
bool st[maxn];  //st[x] 存储的 x 是否被筛除
void get_primes(int n)
{
    
    
    for(int i = 2; i <= n; i++)
    {
    
    
        if(!st[i]) primes[cnt++] = i;
        for(int j = 0; primes[j] <= n/i; j++)
        {
    
    
            st[primes[j]*i] = true;
            if(i%primes[j] == 0) break;
        }
    }
}
int main()
{
    
    
    get_primes(10000);
    int n;
    while(~scanf("%d",&n) && n)
    {
    
    
        int ans = 0;
        for(int i = 0; i < cnt; i++)
        {
    
    
            if((primes[i]<<1) >= n) break;
            if(!st[n - primes[i]]) ans++;
        }
        printf("%d\n",ans);
    }
    return 0;
}

Dertouzos
题目大意
给你一个整数 n n n d d d,问 1 1 1~ n n n 中有多少数的最大除数是 d d d
输入格式
输入一个 t t t 表示有 t t t 组数据,每组数据两个数 n n n d d d
输出格式
每组数据输出一个答案,占据一行。

结论:必须与 d d d 互质,所求合数要小于 n n n
至于为什么是这个结论,我看别人博客,说的很有道理,但是证明这个结论本人不太会,不过记住这个结论了,hhh

#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn = 1e7 + 7;
int primes[maxn], cnt;  //primes[]存储所有的素数
bool st[maxn];  //st[x] 存储的 x 是否被筛除

void get_primes()
{
    
    
    for(int i = 2; i <= maxn; i++)
    {
    
    
        if(!st[i]) primes[cnt++] = i;
        for(int j = 0; primes[j] <= maxn/i; j++)
        {
    
    
            st[primes[j]*i] = true;
            if(i%primes[j] == 0) break;
        }
    }
}

int main()
{
    
    
    get_primes();
    int n;
    scanf(" %d",&n);
    while(n--)
    {
    
    
        int ans = 0;
        int x,y;
        scanf(" %d%d",&x,&y);
        for(int i = 0; i < cnt; i++)
        {
    
    
            if(primes[i]*y >= x) break;
            ++ans;
            if(y%primes[i] == 0) break;
        }
        printf("%d\n",ans);
    }
    return 0;
}

Max Factor
题目大意
给你 n n n 个合数,输出这些合数中质因子最大的合数,如果质因子相同,则输出较大的合数。
输入格式
第一个数 n n n 表示有 n n n 个数,接着输入 n n n 个数。
输出格式
输出满足题意最大的合数。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 2e4 + 7;
int primes[maxn], cnt;  //primes[]存储所有的素数
bool st[maxn];  //st[x] 存储的 x 是否被筛除
int a[5007];
void get_primes()
{
    
    
    memset(st, true, sizeof st);
    for(int i = 2; i <= 20000; i++)
    {
    
    
        if(st[i]) primes[cnt++] = i;
        for(int j = 0; primes[j] <= 20000/i; j++)
        {
    
    
            st[primes[j]*i] = false;
            if(i%primes[j] == 0) break;
        }
    }
}

int main()
{
    
    
    get_primes();
    int n;
    scanf(" %d",&n);
    for(int i = 0; i < n; i++) scanf("%d",&a[i]);

    int ok = 0,maxx = 1;
    for(int i = cnt - 1; ~i; i--)
    {
    
    
        for(int j = 0; j< n; j++)
        {
    
    
            if(a[j]%primes[i] == 0 )
               {
    
    
                   ok = 1;
                   if(a[j] > maxx) maxx = a[j];
               }
        }
        if(ok) break;
    }
    printf("%d",maxx);
    return 0;
}

欧拉函数

定义:对于 正整数 n n n 它的欧拉函数值是不大于 n n n 的正整数中 n n n互质 的正整数的个数。
时间复杂度 O ( n l o g n ) O(nlogn) O(nlogn)

long long phi(long long n)
{
    
    
    int ans = 0;
    for(long long i = 1; i <= n; i++)
        if(__gcd(i,n) == 1) ans++;
    return ans;
}

时间复杂度 O ( s q r t ( n ) ) O(sqrt(n)) O(sqrt(n))的做法

int phi(int x)
{
    
    
    int res = x;
    for(int i = 2; i <= x/i; i++)
        if(x%i == 0)
        {
    
    
            res = res/i*(i-1);
            while(x%i == 0) x /= i;
        }
    if(x > 1) res = res/x*(x - 1);
    return res;
}

欧拉函数公式:
ϕ ( n ) = n ( 1 − 1 p 1 ) ( 1 − 1 p 2 ) ⋯ ( 1 − 1 p k ) \phi(n) = n(1 - \frac{1}{p1})(1-\frac{1}{p2})\cdots(1-\frac{1}{pk}) ϕ(n)=n(1p11)(1p21)(1pk1)
ϕ ( n ) = n ( p 1 − 1 p 1 ) ( p 2 − 1 p 2 ) ⋯ ( p k − 1 p k ) \phi(n) = n(\frac{p1-1}{p1})(\frac{p2-1}{p2})\cdots(\frac{pk-1}{pk}) ϕ(n)=n(p1p11)(p2p21)(pkpk1)

p 1 p_1 p1 p 2 p_2 p2 ⋯ \cdots p k p_k pk n n n 分解出来的不同质因数。
欧拉函数性质:
ϕ ( 1 ) = 1 \phi(1) = 1 ϕ(1)=1
如果 n = p k n = p^k n=pk p p p 为素数,那么 ϕ ( n ) = p k − p k − 1 = ( p − 1 ) p k − 1 \phi(n) = p^k - p^{k-1} = (p-1)p^{k-1} ϕ(n)=pkpk1=(p1)pk1

利用欧拉函数公式求欧拉表:

int Getphi(int x)
{
    
    
    memset(phi, 0, sizeof phi);
    phi[1] = 1;
    for(int i = 2; i <= x; i++)
    {
    
    
        if(phi[i]) continue;  //该数不是素数
        else
        {
    
    
            for(int j = i; j <= x; j +=i)
            {
    
    
                if(!phi[j]) phi[j] = j;
                phi[j] -= phi[j]/i;  //phi[j] = phi[j]/i*(i-1)
            }
        }
    }
}

埃式筛法求欧拉函数: 利用欧拉函数递推式,时间复杂度 O ( n ) O(n) O(n)
证明:
i % p j = = 0 i \%p_{_j} == 0 i%pj==0
ϕ ( i ) \phi(i) ϕ(i) = i × ( 1 − 1 p 1 ) … ( 1 − 1 p k ) i \times (1- \frac{1}{p1}) \dots(1-\frac{1}{pk}) i×(1p11)(1pk1)
ϕ ( B × i ) = B × i × ( 1 − 1 p 1 ) … ( 1 − 1 p k ) = B × ϕ ( i ) \phi(B\times i) = B \times i \times (1- \frac{1}{p1}) \dots(1-\frac{1}{pk}) = B \times \phi(i) ϕ(B×i)=B×i×(1p11)(1pk1)=B×ϕ(i)

const int maxn = 1e5 + 7;
int primes[maxn], cnt;  //primes[]存储所有素数
int phi[maxn];   //求的是每个欧拉函数的值
bool st[maxn]; //st[x]表示x是否被删除
void Getphi(int n)
{
    
    
    memset(st, 0, sizeof st);
    phi[1] = 1;
    for(int i = 2; i <= n; i++)
    {
    
    
        if(!st[i])  //是素数
        {
    
    
            primes[cnt++] = i;
            phi[i] = i - 1;  //如果数 p 是素数,那么它的欧拉函数是 p - 1 
        }
        for(int j = 0; primes[j] <= n/i; j++)
        {
    
    
            int t = primes[j]*i;
            st[t] = true;  //筛掉
            //利用欧拉函数递推式
            if(i%primes[j] == 0)
            {
    
    
                phi[t] = phi[i]*primes[j];
                break;
            }
            phi[t] = phi[i]*(primes[j] - 1);
        }
    }
    //for(int i = 1; i <= n; i++) phi[i] += phi[i-1];
}

Farey Sequence
题目大意
数列 F n F{_n} Fn 的定义:对于每个正整数 n n n ( n ≥ 2 n \geq 2 n2),满足 0 < a < b ≤ \leq n n n g c d ( a , b ) = 1 gcd(a,b) = 1 gcd(a,b)=1 的不可约有理数 a b \frac{a}{b} ba 的个数。
F 2 F_2 F2 = 1 2 \frac{1}{2} 21 F 3 F_3 F3 = { 1 3 , 1 2 , 2 3 \frac{1}{3}, \frac{1}{2}, \frac{2}{3} 31,21,32}、 F 4 F_4 F4 = { 1 4 \frac{1}{4} 41, 1 3 \frac{1}{3} 31, 1 2 \frac{1}{2} 21, 2 3 \frac{2}{3} 32, 3 4 \frac{3}{4} 43}
计算 F n F_n Fn
输入格式
多组测试,每组测试只占一行,每行一个正整数,表示为 n ( 2 ≤ n ≤ 1 0 6 ) n(2\leq n \leq 10^6) n(2n106),当输入 0 时结束测试。
输出格式
每组测试占一行。
输入样例

2
3
4
5
0

输出样例

1
3
5
9

l o n g long long l o n g long long 保存数据。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 1e6 + 7;
int primes[maxn], cnt;  //primes[]存储所有素数
int phi[maxn];   //求的是每个欧拉函数的值
bool st[maxn]; //st[x]表示x是否被删除
void Getphi(int n)
{
    
    
    memset(st, 0, sizeof st);
    phi[1] = 1;
    for(int i = 2; i <= n; i++)
    {
    
    
        if(!st[i])  //是素数
        {
    
    
            primes[cnt++] = i;
            phi[i] = i - 1;  //如果数 p 是素数,那么它的欧拉函数是 p - 1
        }
        for(int j = 0; primes[j] <= n/i; j++)
        {
    
    
            int t = primes[j]*i;
            st[t] = true;  //筛掉
            //利用欧拉函数递推式
            if(i%primes[j] == 0)
            {
    
    
                phi[t] = phi[i]*primes[j];
                break;
            }
            phi[t] = phi[i]*(primes[j] - 1);
        }
    }
    return;
}
int main()
{
    
    
    Getphi(maxn);
    int n;
    while(~scanf("%d",&n) && n)
    {
    
    
        long long res = 0;
  //      for(int i = 1; i <= n; i++) printf("%d\n",phi[i]);
        for(int i = 1; i <= n; i++) res += phi[i];
        printf("%lld\n",res-1);
    }
    return 0;
}

Bi-shoe and Phi-shoe
题目大意
Bamboo Pole-vault is a massively popular sport in Xzhiland. And Master Phi-shoe is a very popular coach for his success. He needs some bamboos for his students, so he asked his assistant Bi-Shoe to go to the market and buy them. Plenty of Bamboos of all possible integer lengths (yes!) are available in the market. According to Xzhila tradition,

Score of a bamboo = Φ (bamboo’s length)

(Xzhilans are really fond of number theory). For your information, Φ (n) = numbers less than n which are relatively prime (having no common divisor other than 1) to n. So, score of a bamboo of length 9 is 6 as 1, 2, 4, 5, 7, 8 are relatively prime to 9.

The assistant Bi-shoe has to buy one bamboo for each student. As a twist, each pole-vault student of Phi-shoe has a lucky number. Bi-shoe wants to buy bamboos such that each of them gets a bamboo with a score greater than or equal to his/her lucky number. Bi-shoe wants to minimize the total amount of money spent for buying the bamboos. One unit of bamboo costs 1 Xukha. Help him.
输入格式
Input starts with an integer T (≤ 100), denoting the number of test cases.

Each case starts with a line containing an integer n (1 ≤ n ≤ 10000) denoting the number of students of Phi-shoe. The next line contains n space separated integers denoting the lucky numbers for the students. Each lucky number will lie in the range [ 1 , 1 0 6 ] [1, 10^6] [1,106].
输出格式
For each case, print the case number and the minimum possible money spent for buying the bamboos. See the samples for details.
输入样例

3
5
1 2 3 4 5
6
10 11 12 13 14 15
2
1 1

输出样例

Case 1: 22 Xukha
Case 2: 88 Xukha
Case 3: 4 Xukha
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 1e6 + 7;
int primes[maxn], cnt;  //primes[]存储所有素数
int phi[maxn];   //求的是每个欧拉函数的值
bool st[maxn]; //st[x]表示x是否被删除
int a[maxn];
void Getphi(int n)
{
    
    
    memset(st, 0, sizeof st);
    phi[1] = 1;
    for(int i = 2; i <= n; i++)
    {
    
    
        if(!st[i])  //是素数
        {
    
    
            primes[cnt++] = i;
            phi[i] = i - 1;  //如果数 p 是素数,那么它的欧拉函数是 p - 1
        }
        for(int j = 0; primes[j] <= n/i; j++)
        {
    
    
            int t = primes[j]*i;
            st[t] = true;  //筛掉
            //利用欧拉函数递推式
            if(i%primes[j] == 0)
            {
    
    
                phi[t] = phi[i]*primes[j];
                break;
            }
            phi[t] = phi[i]*(primes[j] - 1);
        }
    }
    //有序序列
    phi[1] = 0;
    for(int i = 2; i <= maxn; i++)
        phi[i] = max(phi[i], phi[i-1]);
    return;
}
int main()
{
    
    
    Getphi(maxn);
    int t;
    scanf("%d",&t);
    int cnt = 0;
    while(t--)
    {
    
    
        ++cnt;
        long long res = 0;
        int n, x;
        scanf("%d",&n);
        while(n--)
        {
    
    
            scanf("%d",&x);
            res += lower_bound(phi, phi +maxn, x) - phi;
        }
        printf("Case %d: %lld Xukha\n",cnt,res);
    }
    return 0;
}

GCD Again
题目大意
给你一个数 n n n ( 1 < N < 100000000 ) (1<N<100000000) (1<N<100000000),求 1 1 1~ ( n − 1 ) (n-1) (n1)之中与 n n n 约数大于 1 的数有多少?
输入格式
输入多组数据,输入 n n n 为 0 时输入结束。
输出格式
每组数据占一行。
输入样例

2
4
0

输出样例

0
1

求每个数的欧拉函数,容斥原理 + 欧拉函数,如果用欧拉筛法会造成 M L E MLE MLE,emmm

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int phi(int x)
{
    
    
    int res = x;
    for(int i = 2; i <= x/i; ++i)
    {
    
    
        if(x%i == 0) res = res/i*(i-1);
        while(x%i == 0) x /= i;
    }
    if(x > 1) res = res/x*(x-1);
    return res;
}
int main()
{
    
    
    int x;
    while(~scanf("%d",&x) && x)
        printf("%d\n",x-phi(x)-1);
    return 0;
}

算术基本定理

算术基本定理(唯一分解定理):任何一个大于 1 的自然数 N N N,都可以唯一分解为有限个质数的乘积。
N = p 1 a 1 p 2 a 2 p 3 a 3 , ⋯   , p n a n N = {p_1}^{a1} {p_2}^{a2} {p_3}^{a3},\cdots ,{p_n}^{an} N=p1a1p2a2p3a3,,pnan( p 1 < p 2 < p 3 < ⋯ < p n p_1 < p_2<p_3<\cdots<p_n p1<p2<p3<<pn且均是质数, a 1 , a 2 , a 3 , ⋯   , a n a_1,a_2,a_3,\cdots ,a_n a1,a2,a3,,an 都是正整数)。

基本应用

一个大于1的正整数 n n n,它的标准分解式是 N = p 1 a 1 p 2 a 2 p 3 a 3 , ⋯   , p n a n N = {p_1}^{a_1}{p_2}^{a2} {p_3}^{a3},\cdots ,{p_n}^{an} N=p1a1p2a2p3a3,,pnan
正因数个数为 σ 0 ( N ) = ( 1 + a 1 ) ( 1 + a 2 ) , ⋯ , ( 1 + a n ) \sigma_0(N) = (1+a_1)(1+a_2),\cdots,(1+a_n) σ0(N)=(1+a1)(1+a2)(1+an)
全体正因数之和 σ 1 ( N ) = ( 1 + p 1 + p 1 2 + ⋯ + p 1 a 1 ) × ( 1 + p 2 + p 2 2 + ⋯ + p 2 a 2 ) ⋯ ( 1 + p n + p n 2 + ⋯ + p n a n ) \sigma_1(N) = (1+p_1+{p_1}^2+\cdots+{p_1}^{a_1})\times(1+p_2+{p_2}^2+\cdots+{p_2}^{a_2})\cdots(1+p_n+{p_n}^2+\cdots+{p_n}^{a_n}) σ1(N)=(1+p1+p12++p1a1)×(1+p2+p22++p2a2)(1+pn+pn2++pnan)
由等比数列求和公式: S n = a 1 − a n q 1 − q S_n = \frac{a_1-{a_n}^q}{1-q} Sn=1qa1anq
得出 σ 1 ( N ) = ( p 1 a 1 + 1 − 1 p 1 − 1 ) × ( p 2 a 2 + 1 − 1 p 2 − 1 ) × ⋯ × ( p n a n + 1 − 1 p n − 1 ) \sigma_1(N) = (\frac{ {p_1}^{a_1+1}-1}{p_1-1})\times(\frac{ {p_2}^{a_2+1}-1}{p_2-1})\times\cdots\times(\frac{ {p_n}^{a_n+1}-1}{p_n-1}) σ1(N)=(p11p1a1+11)×(p21p2a2+11)××(pn1pnan+11)
σ 1 ( N ) = 2 N \sigma_1(N)=2N σ1(N)=2N 时称 N N N 为完全数。
证明素数个数无限。

到底是否存在安全奇数至今仍然是个谜emmm
题目大意
每个自然数都可以分解成多个素数的乘积,编写函数,使任意自然数分解成素数的乘积。
输入格式
多组测试,每组测试只有一个自然数 x x x ( 1 ≤ x ≤ 65535 1\leq x \leq 65535 1x65535)
输出格式
对于每个测试样例,输出一个素数乘积的式子。
输入样例

9412

输出样例

2*2*13*181
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn = 1e5 + 7;
bool st[maxn];
int primes[maxn], cnt;
void Get_primes(int x)
{
    
    
    memset(st, true, sizeof st);
    st[0] = st[1] = false;
    for(int i = 2; i <= x; i++)
    {
    
    
        if(st[i]) primes[cnt++] = i;
        for(int j = 0; primes[j] <= x/i; j++)
        {
    
    
            st[primes[j]*i] = false;
            if(i%primes[j] == 0) break;
        }
    }
}
int main()
{
    
    
    Get_primes(maxn);
    //for(int i = 0; i < 10; i++) printf("%d  ",primes[i]);puts("");
    int n;
    while(~scanf("%d",&n))
    {
    
    
        for(int i = 0; i < cnt; i++)
        {
    
    
            while(n%primes[i] == 0)
            {
    
    
                n /= primes[i];
                printf("%d",primes[i]);
                if(n > 1) printf("*");
                if(n == 1) puts("");
            }
            if(st[n])
            {
    
    
                printf("%d\n",n);
                break;
            }
        }
    }
    return 0;
}

The number of divisors(约数) about Humble Numbers
题目大意
一个数含有质因数2,3,5,7就是我们所说的谦虚数,给你一个谦虚数,求有多少个除数。
输入格式
输入多组数据,输入为 0 时停止读入。
输出格式
输出每组数据答案占一行。
输入样例

4
12
0

输出样例

3
6

题目讲解博客链接
思路:由算数基本定理可知 n = p 1 n 1 ∗ p 2 n 2 ∗ … ∗ p n n n n=p_1^{n_1}*p_2^{n2}*…*p_n^{n_n} n=p1n1p2n2pnnn,其中 p 1 , p 2 , … , p n p_1,p_2,…,p_n p1,p2,,pn表示质因数, n 1 , n 2 , … , n n n_1,n_2,…,n_n n1,n2,,nn表示相应质因数的指数,根据乘法原理,则约数的个数为 ( n 1 + 1 ) ( n 2 + 1 ) … ( n n + 1 ) (n_1+1)(n_2+1)…(n_n+1) (n1+1)(n2+1)(nn+1)

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 7;
int main()
{
    
    
    ll a, b, c, d, x;
    while(~scanf("%lld",&x) && x)
    {
    
    
        a = b = c = d = 1;
        while(x%2 == 0) ++a, x/= 2;
        while(x%3 == 0) ++b, x/= 3;
        while(x%5 == 0) ++c, x/= 5;
        while(x%7 == 0) ++d, x/= 7;
        printf("%lld\n",a*b*c*d);
    }
    return 0;
}

如果用分解质因数会TLE,emmm

快速幂

计算 a n a^n an,使用快速幂时间复杂度是 O ( l o g 2 n ) O(log_2n) O(log2n)

ll ksm(ll a, ll b)
{
    
    
    ll ans = 1;
    while(b)
    {
    
    
        if(b&1) ans *= a;
        a *= a;
        b >>= 1;
    }
    return ans;
}

借助幂的 2 i 2^i 2i 运算,以计算 5 13 5^{13} 513 方为例子,指数 13 的二进制位是 ( 1101 ) 2 (1101)_2 (1101)2 5 13 = 5 2 0 × 5 2 2 × 5 2 3 5^{13} = 5^{2^0}\times5^{2^2}\times5^{2^3} 513=520×522×523,通过左移指数 b b b 来遍历所有的二进制位,同时计算 a 2 i a^{2^i} a2i
进入 w h i l e while while 循环前, a = 5 2 0 , n = ( 1101 ) 2 , a n s = 1 a = 5^{2^0}, n = (1101)_2,ans = 1 a=520,n=(1101)2ans=1;
进入 w h i l e while while 循环后:
n n n&1 为真, a n s = 5 2 0 , a = 5 2 0 × 5 2 0 = 5 2 1 , n = ( 110 ) 2 ans = 5^{2^0},a = 5^{2^0}\times5^{2^0}= 5^{2^1},n = (110)_2 ans=520a=520×520=521n=(110)2
n n n&1 为假, a n s = 5 2 0 , a = 5 2 1 × 5 2 1 = 5 2 2 , n = ( 11 ) 2 ans = 5^{2^0},a = 5^{2^1}\times 5^{2^1}= 5^{2^2},n = (11)_2 ans=520a=521×521=522n=(11)2
n n n&1 为真, a n s = 5 2 0 × 5 2 2 , a = 5 2 2 × 5 2 2 = 5 2 3 , n = ( 1 ) 2 ans = 5^{2^0}\times5^{2^2},a = 5^{2^2}\times 5^{2^2} = 5^{2^3},n = (1)_2 ans=520×522a=522×522=523n=(1)2
n n n&1 为真, a n s = 5 2 0 × 5 2 2 × 5 2 3 , a = 5 2 3 × 5 2 3 = 5 2 4 , n = 0 ans = 5^{2^0}\times 5^{2^2}\times 5^{2^3},a = 5^{2^3}\times 5^{2^3} = 5^{2^4},n = 0 ans=520×522×523a=523×523=524n=0

关于取模:

计算加法和乘法时,每做一次相加或者相乘进行一次模运算。
计算减法时先加上模,再计算减法再取模。

Rightmost Digit
题目大意
输入一个 n n n,表示 n n n^n nn 方,求其个位是多少?
输入格式
输入一个 T T T,表示有 T T T 组数据,每组数据输出其个位数。
输出格式
输出每组数据占一行
输入样例

2
3
4

输出样例

7
6
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 7;
const int mod = 10;
ll ksm(ll a, ll b)
{
    
    
    ll ans = 1;
    while(b)
    {
    
    
        if(b&1) ans = (ans%mod*a)%mod;
        b >>= 1;
        a =(a%mod*a)%mod;
    }
    return ans%mod;
}
int main()
{
    
    
    int t;
    scanf("%d",&t);
    while(t--)
    {
    
    
        int x;
        scanf("%d",&x);
        printf("%d\n",ksm(x,x));
    }
    return 0;
}

矩阵快速幂

矩阵快速幂学习博客
矩阵快速幂之矩阵构造的基本思想

Number Sequence
题目大意
f ( 1 ) = 1 , f ( 2 ) = 1 , f ( n ) = ( A ∗ f ( n − 1 ) + B ∗ f ( n − 2 ) ) m o d f(1) = 1,f(2) = 1,f(n) = (A*f(n-1)+B*f(n-2))mod f(1)=1f(2)=1f(n)=(Af(n1)+Bf(n2))mod 7,给出 A , B , n A,B,n ABn,求 f ( n ) f(n) f(n)
输入格式
输入多组数据,每组数据三个数,分别表示 A , B , n ( 1 ≤ A , B ≤ 1000 , 1 ≤ n ≤ 1000000000 ) A,B,n(1\leq A,B \leq 1000,1\leq n \leq 1000000000) ABn(1A,B1000,1n1000000000)
输出格式
每行对应输出一个结果。
输入样例

1 1 3
1 2 10
0 0 0

输出样例

2
5

( f ( n ) , f ( n − 1 ) ) = ( f ( 2 ) f ( 1 ) ) ( A 1 B 0 ) n − 2 (f(n),f(n-1)) = (f(2) f(1)) \left ( \begin{matrix} A & 1 \\ B & 0 \end{matrix} \right )^{n-2} (f(n)f(n1))=(f(2)f(1))(AB10)n2

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 7;
struct Matrix{
    
    
    int m[2][2];
};
Matrix MatrixMultiply(Matrix a, Matrix b)
{
    
    
    Matrix ans;
    ans.m[0][0] = ans.m[0][1] = ans.m[1][0] = ans.m[1][1] = 0;
    for(int i = 0; i < 2; i++)
        for(int j = 0; j < 2; j++)
            for(int k = 0; k < 2; k++)
                ans.m[i][j] =(ans.m[i][j]+(a.m[i][k]*b.m[k][j])%mod)%mod;
    return ans;
};
int ksm(int b, int A, int B)
{
    
    
    struct Matrix ans, a;
    ans.m[0][0] = ans.m[1][1] = 1;
    ans.m[0][1] = ans.m[1][0] = 0;
    a.m[0][0] = A; a.m[0][1] = 1;
    a.m[1][0] = B; a.m[1][1] = 0;
    while(b)
    {
    
    
        if(b&1) ans = MatrixMultiply(ans,a);
        a = MatrixMultiply(a,a);
        b >>= 1;
    }
    return (ans.m[0][0] + ans.m[1][0])%mod;
}
int main()
{
    
    
    int A, B, n;
    while(~scanf("%d%d%d",&A,&B,&n) && A && B && n)
    {
    
    
        if(n == 1 || n == 2) printf("1\n");
        else printf("%d\n",ksm(n-2,A,B));
    }
    return 0;
}

2019牛客多校第五场B十进制快速幂
题目大意
在这里插入图片描述
输入格式
在这里插入图片描述
输出格式
在这里插入图片描述
写成矩阵快速幂的形式,相当于求转移矩阵的 n n n 次幂。
由于 n n n 过大,只能用字符串形式保存,如果转成二进制复杂度过高,就直接用十进制好了。
其实十进制快速幂和二进制几乎一样,都是倍增的思想。

ll qpow(ll a, ll b, ll p)
{
    
    
    ll ret = 1;
    while(b)
    {
    
    
        if(b&1) ret = ret*a%p;
        a = a*a%p;
        b >>= 1;
    }
    return ret;
}

inline ll shi_pow(ll a, ll b, ll p)
{
    
    
    ll ret = 1;
    while(b)
    {
    
    
        ll yu = b%10;
        if(yu) ret = ret*qpow(a,yu,p)%p;
        a = qpow(a, 10, p);
        b /= 10;
    }
    return ret;
}

相关题目博文链接

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll mod;
inline ll mul(ll x, ll y, ll p){
    
    
    return x*y%p;
}
struct mat{
    
    
    ll M[3][3];
    mat(){
    
    M[1][1] = M[1][2] = M[2][1] = M[2][2] = 0; }
    mat friend operator *(mat a,mat b){
    
    
        mat res;
        for(int k = 1; k <= 2; k++)
            for(int i = 1; i <= 2; i++)
                for(int j = 1; j <= 2; j++)
                    res.M[i][j] += a.M[i][k]*b.M[k][j];
        for(int i = 1; i <= 2; i++)
            for(int j = 1; j <= 2; j++)
                res.M[i][j] %=mod;
        return res;
    }
    mat friend operator ^(mat a, int x){
    
    
        mat res; res.M[1][1] = res.M[2][2] = 1LL;
        while(x){
    
    
            if(x&1) res = res*a;
            a = a*a;
            x >>= 1;
        }
        return res;
    }
};
char c[1000007], x[1000007];
int main()
{
    
    
    int T; ll x0, x1, A, B, N; T = 1;
    while(T--){
    
    
        scanf("%lld%lld%lld%lld",&x0,&x1,&A,&B);
        scanf("%s%lld",c+1,&mod);
        int len = strlen(c+1);
        for(int i = 1; i <= len; i++) x[i] = c[i] - '0';
        x[len]--;
        for(int i = len; i; i--)
            if(x[i] < 0) x[i-1]--, x[i] += 10;

        mat base, a, ans;
        ans.M[1][1] = ans.M[2][2] = 1;
        base.M[1][1] = A%mod; base.M[1][2] = B%mod; base.M[2][1] = 1LL;
        a.M[1][1] = x1%mod, a.M[2][1] = x0%mod;
        for(int i = len; i; i--){
    
    
            if(x[i]) ans = ans*(base^x[i]);
            base = base^10;
        }
        ans = ans*a;
        printf("%lld\n",ans.M[1][1]%mod);
    }
    return 0;
}

在这里插入图片描述

Switch Game
Sequence
剩下两个题供读者自行解决,题目链接已经发放出来,博主学习矩阵快速幂,后续无聊可能放出代码和题解,丢了几篇学习的博客,板子是付队的板子,QaQ,害,数学不太好就这样吧。

猜你喜欢

转载自blog.csdn.net/Edviv/article/details/108466138