数论在ACM中的几个常见应用结论


1、欧几里得算法(辗转相除法):

关键在于恒等式gcd(a, b) = gcd(b, a mod b),边界条件为gcd(a, 0) = a,程序如下:

int gcd(int a, int b){

     return b == 0 ? a : gcd(b, a % b);

}

虽然是递归,但是保证不会栈溢出。可以证明,gcd函数的递归层数不超过4.785 * lgN + 1.6723,其中N = max{a, b}

ab的最小公倍数lcm(a, b)a / gcd(a, b) * b,不可以写为a * b / gcd(a, b),因为a * b可能会溢出。

2、素数定理:π(x) ~ x / ln(x),其中π(x) 表示不超过x的素数个数,表示近似的意思。

素数筛选法:

memset(vis, 0, sizeof(vis));

for(int i = 2; i <= n; i++)

    for(int j = i * 2; j <= n; j += i)

        vis[j] = 1;

我们可以对这份代码进行改进:

memset(vis, 0, sizeof(vis));

int m = sqrt(n + 0.5);

for(int i = 2; i <= m; i++)

    if(!vis[i])

        for(int j = i * i; j <= n; j++)

            vis[j] = 1;

int cnt = 0;

for(int i = 2; i <= n; i++)

    prime[cnt++] = i;

3、同余模公式:

(a + b) mod n = ((a mod n) + (b mod n)) mod n

a * b mod n = (a mod n) * (b mod n) mod n

(a - b) mod n = ((a mod n) - (b mod n) + n) mod n

a * b mod n代码如下:(当n = 10 ^ 9时,a * b mod n一定在int范围内,但a mod nb mod n的乘积可能会超过int,需要用long long保存中间结果)

int mul_mod(int a, int b, int n){

    a %= n;

    b %= n;

    return (int)((long long)a * b % n);

}

4、由组合数公式C(n, k) = n! / (k! * (n - k)!),得到等式C(n, k) = ((n - k + 1) / k) * C(n, k - 1)

5、数论中的计数问题:

(1)给出正整数n的唯一分解式n = p1 ^ a1 * p2 ^ a2 ... pk ^ ak,求n的正约数个数。

对于n的某个素因子pi,它在所求约数中的指数可以是01 2...aiai + 1种情况,所以n的正约数个数为:(a1 + 1) * (a2 + 1) ... (ak + 1)

(2)给出正整数n的唯一分解式n = p1 ^ a1 * p2 ^ a2 ... pk ^ ak,求123...n中与n互素的数的个数。

由容斥原理得到欧拉函数:φ(n) = n * (1 - 1 / p1) * ( 1 - 1 / p2) ... (1 - 1 / pk)

代码如下:

int euler_phi(int n){

    int m = sqrt(n + 0.5);

    int ans = n;

    for(int i = 2; i <= m; i++)

        if(n % i == 0){

            ans = ans / i * (i - 1);

            while(n % i == 0) n /= i;

        }

    if(n > 1) ans = ans / n * (n - 1);

}

如果需要求出1~n中所有数的欧拉函数值,可以用于筛选法求素数类似的方法:

void phi_table(int n, int* phi){//比较难理解,注意好好理解!!

    for(int i = 2; i <= n; i++) phi[i] = 0;

    phi[1] = 1;

    for(int i = 2; i <= n; i++)

        if(!phi[i])

            for(int j = i; j <= n; j += i) {

                if(!phi[j]) phi[j] = j;

                phi[j] = phi[j] / i * (i - 1);

            }

}

6、费马小定理及欧拉定理

费马小定理:若p为质数,且(a, p) = 1,则a ^ (p - 1) % p == 1;即若p为质数,且a, p互质,那么 a(p-1)次方除以p的余数恒等于1

欧拉定理:a ^ (φ(n)) % n == 1,其中φ(n)为欧拉函数,φ(n) = n * (1 - 1 / p1) * ( 1 - 1 / p2) ... (1 - 1 / pk)p1, p2, ..., pkn的的唯一分解式的素因子。

7、编码与解码:

任给一个字符串,求它按照字典序的编号<编码>;或者反过来给一个字符串的字典序编号,求该字符串<解码>

结论:设字符一共有k类,个数分别为n1, n2, ..., nk,则这个多重集的全排列个数为(n1 + n2 + ... + nk)! / (n1! * n2! ... nk!)

例如有两个a、一个b和一个c组成的串,求串caba的编号;以及求编号为8的字符串。

(1)设输入串为S,记d(S)S的各个排列中,字典序比S小的串的个数,记f(x)表示多重集x的全排列个数。

d(S) = d(caba) = f(cba) + f(caa) + d(aba) = f(cba) + f(caa) + d(ba) = f(cba) + f(caa) + f(b) + d(a) = f(cba) + f(caa) + f(b)

f(cba) = (1 + 1 + 1)! / (1! * 1! * 1!) = 6f(caa) = (1 + 2)! / (1! * 2!) = 3f(b) = 1

d(S) = 10

所以串caba的编号为11

(2)因为比所求字符串小的字符串第一个字符可取abc,若分别取定后,由f(cba) = 6f(caa) = 3f(aab) = 3且所给编号为8得知:

所求字符串第一个字符为b(因为6 + 3 >= 8);比所求字符串小的字符串第二个字符可取ac,若分别取定后,由f(ca) = 2f(aa) = 1得知:

所求字符串第二个字符为a(因为6 + 2 >= 8);比所求字符串小的字符串第三个字符可取ac,若分别取定后,由f(c) = 1f(a)  = 1得知:

所求字符串第三个字符为c(因为6 + 1 + 1 = 8),从而第四个字符为a,故所求字符串为baca

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值