算法竞赛入门经典第十章知识点总结1

欧几里得算法

算法引入:除法表达式

题目描述

给出一个这样的除法表达式:X1/X2/X3/·····/Xk,其中Xi是正整数。除法表达式应当按照从左到右的顺序求和。但是可以在表达式中嵌入括号以改变计算方式。
输入X1/X2/X3/·····/Xk,判断是否可以通过添加括号,使得表达式的值为整数。K <= 10000, Xi <= 109
不难发现,无论括号怎么摆,怎么加,X1必然在分子,X2必然在分母。
关键是其他X3…Xk

书本直接给出了
E = X1/(X2/X3/····Xk) = (X1/X3/·····/Xk) / X2
再去判断E是否为整数。
书本并没有解释为什么这样做可以。
换句话说,如果将除了X2其他的Xi都写在分子,如果得到的E不为整数,那么无论怎么添加括号都不会是整数。或许会有疑问,那我把X3,X4,Xi,放分母试试不行吗?事实上不行。
粗略证明
反证法
E * X2 = X1X3·····Xk E 不为整数
现在把Xi(i != 1)放在分母
此时式子等于
A= X1···Xi-1Xi+1···Xk 1
B = X2Xi 2
此时
E * X2Xi = X1···Xi-1Xi+1···Xk
E * X2 = X1X3·····Xk
第二条式子两边乘上Xi,得:
E = E * Xi2
如果E不为整数,E不可能为整数。因为Xi2必定也为正整数。
所以矛盾。所以当E不为整数时,表达式X1/X2/X3/·····/Xk不可能得到整数结果。
同理可证B’ = X2 * Xi ··· * Xk

欧几里得算法(辗转相除法)代码表示

/*
 *gcd(a,b) = gcd(b, a % b)
 *边界情况gcd(a, 0) = a
 */
 int gcd(int a, int b) {
 	return b == 0 ? a, gcd(b, a % b);
 }

除法表达式代码

int judge(int *X) {
	X[2] /= gcd(X[1, X[2]);
	for(int i = 3; i <= k; ++i) {
		X[2] /= gcd(X[i], X[2]);
	}
	return X[2] == 1;
}

Eratosthenes筛法

算法引入

无平方因子的数
题目描述:给出正整数n和m,区间[n,m]内的“无平方因子”的数有多少个?
整数p无平方因子当且仅当不存在 k>1,使得p是k2的倍数。
我看到书本下面一大篇幅来介绍素数筛法。是不是和素数有关系。
重新看一下问题。
整数p无因子当且仅当不存在 k > 1,使得p时k的倍数。
没错,就和判断p是不是素数很相似。

代码解释

int m = sqrt(n + 0.5); //求根号的意思
int v[maxn];
memset(v, 0, sizeof(v));
for(int i = 2; i <= m; ++i) {
	if(!v[i]) {
	for(int j = i * i; j <= n; j += i) {
		v[j] = 1;
	}

关于代码有两点要解释的

  • 为什么进入内部循环要判定是不是素数?
    • 因为小于目前素数的合数必然被更小的素数筛了。
  • 内部循环为什么从i * i开始
    • 因为小于i * i的合数都被前面的素数筛了。
      简略证明
      设小于i * i 的合数为m,m的因数a,b
      必然有a >= m \sqrt{m} m && b <= m \sqrt{m} m
      或者有a <= m \sqrt{m} m && b >= m \sqrt{m} m
      设t = m \sqrt{m} m
      m必然是小于等于t的数的倍数,由此他们就被筛了。

ps:hhh,上面的一切解释都可以用唯一分解定理。

平方因子代码

/*
 *无平方因子plus
 *如果说一个数组来存储根号m前面的素数,开的数组量肯定不同超过int的最大值。
 *可是另外一个数组来存储平方,像筛素数那样,起码开10^12次方,开不了这么大。
 *所以在筛平方因子时,做了个映射。 m - n必然超不过10^7,所以这样i - n;具体代码32行。
 */
#include <iostream>
#include <cstring>
#include <cmath>
using namespace std;
const int maxn = 10000005;
long long value[maxn];
int visit[maxn];
void ini(long long n, long long m) {
	memset(value, 0, sizeof(value));
	memset(visit, 0, sizeof(visit));	
}
void Solve(long long n, long long m) {
	int ob1 = sqrt(m + 0.5), ob2 = sqrt(sqrt(m + 0.5) + 0.5);
	for(int i = 2; i <= ob2; ++i) {
		if(!visit[i]) {
			for(long long j = i * i; j <= ob1; j += i) {
				visit[j] = 1;
			}
		}
	}
	for(int i = 2; i <= ob1; ++i) {
		if(!visit[i]) {
			int index = i * i;
			for(int j = index; j <= m; j += index) {
				if(j >= n) {
					value[j - n] = 1;
				}
			}
		}
	}
}
int main() {
	long long n, m;
	cin >> n >> m;
	ini(n, m);
	Solve(n, m);
	int ob = m - n + 1, tol = 0;	
	for(int i = 0; i < ob; ++i) {
		if(!value[i]) tol++;
	}	
	cout << tol << endl;
	return 0;
}

扩展欧几里得算法

定义

对于找出一对整数x,y,使得等式ax + by = gcd(a,b)成立。

代码

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, y, x);
		y -= x * (a / b);
	}
}

这个链接是讲如何将x,y还原的博客
上面的博客讲的很详细。但是刘汝佳大佬的代码我还没看得太懂。

同余与模算术

公式

(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) < 0
ab mod n = ((a mod n) * (b mod n)) mod n

大整数取模

大整数通常指超过了超过了基础数据结构的最大值。例如C语言就是long long.

int ans = 0;
for(int i = 0; i < str.len; ++i) {
	ans = (int)((long long)ans * 10 + str[i] - '0') % m);
} 

幂取模

和求快速幂的思想差不多

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

模线性方程组

输入正整数a,b,n,解方程 a x ≡ b ( m o d n ) ax \equiv b(modn) axb(modn)
同余方程:
a ≡ b ( m o d n ) a\equiv b(mod n) ab(modn)
充要条件:a - b = nk(k 为 整数)
简略证明:
a mod n = b mod n
a = c1n + m
b = c2n + m
a - b = (c1 - c2)n

由同余方程,可得模线性方程组ax - b = ny
得:ax - ny = b
接下来就和上面求扩展欧几里得一样了。

a关于模n的逆

b = 1时, a ≡ 1 ( m o d n ) a \equiv 1(modn) a1(modn)的解为a关于模n的逆
要想逆存在,即
ax - ny = 1要有解。
由扩展欧几里得,
ax - ny = gcd(a, n)
所以1 = kgcd(a, n)
所以gcd(a, n) = 1

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Greatljc

你的鼓励是我最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值