主要内容来自《算法竞赛入门 第二版》, 这里只是我的理解以及总结。也许会有很多不足,欢迎提出不同意见,谢谢!
一. 欧几里德算法
两个整数的最大公约数等于其中较小的那个数和两数相除余数的最大公约数。
代码(非递归):
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 = y2 且 y1 = 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;
}