能力综合提升-数学-2
0x04 裴蜀定理
内容
证明
对于第一点,
对于第二点,记
设
又
那么就是
又由第一点
那么
推论
例题
给定互质的正整数
0x05 乘法逆元
扩展欧几里得算法
1
2
3
4
5
void exgcd(int a, int b, int &x, int &y)
{
if(b == 0) x = 1, y = 0;
else exgcd(b, a % b, y, x), y -= a / b * x;
}
用来求解形如
那么原方程的一个解就是
要求出所有解,我们显然可以注意到,对
如果我们想求最小的正数 x = (x % b + b) % b
,它可以处理负数。
回到求逆元问题上来,
1
2
3
4
5
int inv(int a, int b)
{
int x, y; exgcd(a, b, x, y);
return (x % b + b) % b;
}
例题
求解
不妨记
考虑 exgcd
的原理,是用来求解形如
引理:对于不定方程
,记 。
若
则方程有整数解,且可以通过 exgcd
求出;若
则方程无解。 证明:
对于第一种情况,考虑
的方程,由裴蜀定理可知必存在 满足 。因此方程的通解是 。 记 ,则两边同乘 有 ,也就找到了特解 。 对于第二种情况,反证,假设存在这样的解使
。由于 ,可化为 ,即 ,矛盾。
于是,当 exgcd
解出
不过需要注意:
的情况,最好使用x = (x % L + L) % L
这样的语句来保证取值非负。exgcd
求出解后,剩余系变成了 ,对于后面的非负操作要对 修改。#define int ll
。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
signed main()
{
ll x, y, m, n, L;
read(x, y, m, n, L);
ll u = ((x - y) % L + L) % L;
ll v = ((n - m) % L + L) % L;
ll d = gcd(v, L);
if(u % d != 0)
puts("Impossible"), exit(0);
ll a, b;
exgcd(v, L, a, b);
L /= d;
a = (a % L + L) % L;
ll ans = a * (u / d) % L;
write('\n', ans);
return 0;
}
费马小定理
注意:费马小定理的逆定理不成立,即不能从
由于证明方法非常美妙,我打算写三种。
法一:简化剩余系
仅证
考虑
互质:
互不相同:反证,
这两个性质保证了
证毕。
法二:二项式定理
考虑二项式系数
再考虑
(注:这个结论也可以由数学归纳法证明)
因此
证毕。
法三:拉格朗日定理
当
考虑由
证毕。
另外,费马小定理实质上是欧拉定理的一种特殊情况,下文会谈。
回到求逆元问题上来,由于
1
2
3
4
int inv(int a, int p)
{
return fastpow(a, p - 2, p);
}
可以发现能用费马小定理的情况一定能用扩展欧几里得算法得出(
递推线性求逆元
起始条件
记
其中
另外,写代码的时候,我们不希望它计算后是个负数,因此一般写成
1
2
3
4
5
6
7
int inv[Max];
void Pre(int Max, int p)
{
inv[1] = 1;
F(i, 2, p - 1)
inv[i] = (p - p / i) * inv[p % i] % p;
}
0x06 欧拉定理 & 扩展欧拉定理
欧拉定理
若
证明非常类似费马小定理的证明。
把费马小定理证明的法一中的
把费马小定理证明的法三中的
当
扩展欧拉定理
第一种情况可以由欧拉定理直接得到,第二种情况的意思是无需使用扩展欧拉定理计算,时间复杂度可以接受,第三种情况证明太长不看。
例题
求
对于指数很大的数要首先想到扩展欧拉定理。由于
注意看,此时对
可知,最坏情况下每两次迭代
1
2
3
4
5
6
int f(int p)
{
if(p == 1) return 0;
int Phi = phi(p);
return fastpow(2, f(Phi) + Phi, p);
}
0x07 中国剩余定理 & 扩展中国剩余定理
中国剩余定理
中国剩余定理是用来求解如下形式的一元线性同余方程组:
这里
推导过程
考虑把方程组分解,若能分别解出下列
就能得到
以第一个方程为例,可以进一步转换:
求出了
记 exgcd
求解。
于是便可以按这种方法,求出上面的
结论
计算所有模数的积
对于第
个方程,计算
exgcd
计算 在模 意义下的逆元计算
(不要对 取模)
唯一解为
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#define int __int128
signed main()
{
read(n);
F(i, 1, n)
read(b[i], a[i]), B *= b[i];
int ans = 0;
F(i, 1, n)
{
int p = B / b[i];
ans += a[i] * p * inv(p, b[i]);
// 因为用了 __int128 很充裕,如果是 long long 要龟速乘且每步取模
}
write('\n', ans % B);
return 0;
}
扩展中国剩余定理
中国剩余定理无法处理模数不互质的情况,这是中国剩余定理的原理所决定的。它的解中 “
推导过程
仅考虑两个方程的方程组,
它等价于
利用 exgcd
可以求出不定方程的解(前面例题有讲),于是构造出了一个这样的
显然它的解是
这就是两个同余方程合并为一个的结果。
因此,反复的将两个方程合并为一个,最终剩余的结果就是解系。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#define int __int128
typedef pair<int, int> pii;
#define mp(a, b) make_pair(a, b)
pii a[maxn];
pii merge(pii x, pii y)
{
int a1 = x.first, b1 = x.second;
int a2 = y.first, b2 = y.second;
int u = a2 - a1, d = gcd(b1, b2);
if(u % d != 0)
assert(1);
int l = b1 * b2 / d;
int p, q;
exgcd(b1, b2, p, q);
p = (p % b2 + b2) % b2;
p *= u / d;
int ans = a1 + p * b1;
ans = (ans % l + l) % l;
return mp(ans, l);
}
signed main()
{
read(n);
F(i, 1, n)
read(a[i].second, a[i].first);
F(i, 2, n)
a[i] = merge(a[i - 1], a[i]);
write('\n', a[n].first);
return 0;
}
0x08 BSGS & 扩展 BSGS
BSGS
BSGS(Baby-Step Giant-Step,大步小步算法)常用于求解离散对数问题。具体的,它能在
其中
推导过程
正如“大步小步”的名字一样,主要在于先小步小步的处理,然后大步大步的处理,跟分块相似。
令
预处理每个