[理论储备] 数论初步

主要内容来自《算法竞赛入门 第二版》, 这里只是我的理解以及总结。也许会有很多不足,欢迎提出不同意见,谢谢!

一. 欧几里德算法

两个整数的最大公约数等于其中较小的那个数和两数相除余数的最大公约数。

代码(非递归):

int gcd (int a, int b) {
	int c;
	while (b != 0) {
		c = a;
		a = b;
		b = c%a;
	}
	return a;
}

代码(递归):

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

二. 唯一分解定理

任何一个大于1的自然数 ,如果N不为质数,都可以唯一分解成有限个质数的乘积

练习题目:Minimum Sum LCM
我的代码:

#include<bits/stdc++.h>

using namespace std;

int main () {
	unsigned long long n;
	int cnt;
	for (int t = 1; cin >> n && n != 0; t++) {
		cnt = 0;
		if (n == 1) {
			cout << "Case " << t << ": 2" << endl;
			continue;
		}
		unsigned long long ans = 0, m = n, l;
		for (long long i = 2; i <= sqrt(m); i++) {
			l = 0;
			while (m % i == 0) {
				l++; 
				m = m/i;
			}
			if (l > 0) {
				cnt++;
				ans += pow(i, l);
			}
		}
		if (ans == 0) {
			ans = n + 1;
		} else if (m != 1) {
			ans += m;
		} else if (cnt == 1) {
			ans = ans + 1;
		}
		cout << "Case " << t << ": " << ans << endl;
	}
	return 0;
}

三. Eratosthenes筛法

埃拉托色尼筛选法是针对自然数列中的自然数而实施的,用于求一定范围内的质数

我的理解是每次都把素数的倍数删除,留下来的就是素数

代码:

void eratosthenes (int b) {
	for (int i = 2; i <= b; i++) {
		if (notPrimes[i]) continue;
		for (int j = 2; j * i <= b; j++) {
			notPrimes[j*i] = true;
		}
	}
}

练习题目: Choose and divide UVA - 10375
我的代码:

#include<cstdio>
#include<cmath>

using namespace std;

const int MAXN = 10000;

bool notPrime[MAXN+10]; // 不是素数时为true,是素数时为false 
int isPrime[MAXN], cnt[MAXN], primeLen = 0;

// 生成2~MAXN中所有的素数 
void eratosthenes () {
	for (int i = 2; i < MAXN; i++) {
		if (notPrime[i]) continue;
		isPrime[primeLen++] = i;
		for (int k = 2; i*k < MAXN; k++) {
			notPrime[i*k] = true;
		}
	}
}

void changeCnt (int n, int d) {
	for (int i = 2; i <= n; i++) {
		for (int j = 0, num = i; num != 1; j++) {
			while (num % isPrime[j] == 0 && num != 1) {
				cnt[j] += d;
				num = num/isPrime[j];
			}
		}
	}
}

int main () {
	int p, q, r, s;
	eratosthenes();
	while (~scanf("%d %d %d %d", &p, &q, &r, &s)) {
		changeCnt(p, 1);
		changeCnt(q, -1);
		changeCnt(p-q, -1);
		changeCnt(s, 1);
		changeCnt(r-s, 1);
		changeCnt(r, -1);
		double ans = 1;
		for (int i = 0; i < primeLen; i++) {
			ans = ans*pow(isPrime[i], cnt[i]);
			cnt[i] = 0;
		}
		printf("%.5lf\n", ans);
	}
	return 0;
}

四. 扩展欧几里德算法

已知整数a、b,扩展欧几里得算法可以在求得a、b的最大公约数的同时,能找到整数x、y(其中一个很可能是负数),使它们满足贝祖等式: ax+by = gcd(a, b) =d

代码:

// d是最大公约数
void gcd (int a, int b, int &d, int &x, int &y)  {
	if (!b) {
		d = a;
		x = 1;
		y = 0;
	} else {
		gcd(b, a%b, d, x, y);
		int x2 = x, y2 = y;
		x = y2;
		y = x2 - (a/b)*y2;
	}
}

证明:
1° 当b != 0
∵ a * x1 + b * y1 = gcd(a, b)
又 b * x2 + (a % b) * y2 = gcd(b, a % b)
∴ a * x1 + b * y1 = b * x2 + (a%b) * y2
∴ a * x1 + b * y1 = b * x2 + (a - ⌊a/b⌋*b) * y2
∴ a * x1 + b * y1 = a * y2 + b(x2 - ⌊a/b⌋ * y2)
x1 = y2y1 = x2 - ⌊a/b⌋ * y2
2° 当 b == 0
x1 = 1且 y1 = 0

练习题目: Disgruntled Judge UVA - 12169

思路:
∵ x2 := (a * x1 + b ) % 10001
又 x3 := (a * x2 + b) % 10001
∴ x3 := (a * a * x1 + (a + 1) * b ) % 10001
设 y := ⌊ (a * a * x1 + (a + 1) * b) / 10001 ⌋
∴ x3 + y * 10001 := a * a * x1 + (a + 1) * b
∴ y * 10001 - (a+1) * b := a * a * x1 - x3
且 a * a * x1 和 x3 都是已知的
那么这就是一个扩展欧几里得算法
那么我们只需要枚举a,然后算出b,看这个序列是否矛盾就可以了

我的代码:

#include<cstdio>

using namespace std;

const int MAXT = 105;

long long T, p[MAXT], ans[MAXT], a, b;

void exgcd(int a, int b, long long& d, long long& x, long long& y) {
	if (!b) {
		d = a;
		x = 1;
		y = 0;
	}
	else{
		exgcd(b, a%b, d, y, x);
		y -= x*(a/b);
	}
}

int nextX (int x) {
	return (int) ((long long) x*a+b)%10001;
}

int main () {
	scanf("%d", &T);
	for (int t = 0; t < T; t++) {
		scanf("%d", &p[t]);
	}
	bool ok = false;
	for (a = 0; a < 10001 && !ok; a++) {
		long long bi = (p[1] - a*a*p[0])%10001;
		// 求b 
		long long x, y, d;
		exgcd(10001, a+1, d, x, y);
		if ((p[1] - a*a*p[0]) % d != 0) continue;
		b = (int) ((y*bi)/d)%10001;
		
		// 验证 
		for (int t = 0; t < T-1; t++) {
			ans[t] = nextX(p[t]);
			if (p[t+1] != nextX(ans[t])) break;
			if (t == T-2) {
				ans[t+1] = nextX(p[t+1]);
				ok = true;
			}
		}
	}
	for (int t = 0; t < T; t++) {
		printf("%d\n", ans[t]);
	}
	return 0;
} 

五. 模运算

( a + b ) % n = ( ( a % n ) + ( b % n ) ) % n ①
( a - b ) % n = ( ( a % n ) - ( b % n) + n ) % n ②
( a * b ) % n = ( ( a % n ) * ( b % n ) ) % n ③

1.大整数取模(使用①):

题目:输入正整数n和m,输出n mod m的值。n ≤ 10e100,m≤10e9。
我的解答:

long long bigIntegerMod (char *bigInteger, int divisor) {
	long long ans = 0;
	for (int i = 0; i < strlen(bigInteger); i++) {
		ans = (ans * 10 + bigInteger[i] - '0') % divisor;
	}
	return ans;
}

2.幂取模(使用③):

题目:输入正整数a、 n和m,输出a^n mod m的值。a, n, m≤10e9
代码:

int powerMod (int a, int n, int m) {
	if (n == 0) return 1;
	int x = powerMod(a, n/2, m);
	long long ans = (long long)x * x %m;
	if (n%2 == 1) {
		ans = ans*a%m;
	} 
	return (int) ans;
}

练习题目: Colossal Fibonacci Numbers! UVA - 11582
我的代码:

#include<iostream>

#define ULL unsigned long long // 数据类型为 unsigned long long
using namespace std;

const int MAXN2 = 1000000;

int fab[MAXN2+100], N;
ULL A, B;

ULL powerMod (ULL a, ULL n, ULL m) {
	if (n == 0) return 1;
	ULL x = powerMod(a, n/2, m);
	ULL ans = (x*x)%m;
	if (n%2 == 1) {
		ans = ans*a%m;
	} 
	return ans;
}

int main () {
	int T;
	fab[1] = fab[2] = 1;
	cin >> T;
	while (T--) {
		cin >> A >> B >> N;
		if (N == 1) {
			cout << 0 << endl; // 如果n=1结果就一定为0
			continue;
		}
		ULL cycLen;
		for (int i = 3; i <= MAXN2; i++) {
			fab[i] = (fab[i-1] + fab[i-2])%N;
			if (fab[i] == 1 && fab[i-1] == 1) {
				cycLen = i-2; // 此时开始循环
				break;
			}
		}
		ULL index = powerMod(A%cycLen, B, cycLen);
		cout << fab[index] << endl;
	}
	return 0;
}

  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值