- 基本介绍
- 模板题目
- 代码实现
基本介绍
先安利一个博客 非常好 镜外之主
数论倒数 又称逆元 (a的倒数在数论中不是1/a)
我们知道 下面这三个是对的
(a + b) % p = (a%p + b%p) %p
(a - b) % p = (a%p - b%p) %p
(a * b) % p = (a%p * b%p) %p
但是这个 不行
(a / b) % p = (a%p / b%p) %p
对于一些题目 我们必须在中间过程中进行求余 那如果这个算式中出现除法 我们该怎么办 于是引入逆元
在这个式子a*x = 1 (mod p)中 x一定等于1/a吗?
不一定 但是x确实a的倒数 只不过加了一个求余条件 所以x叫做 a关于p的逆元 用inv(a)来表示
比如2 * 3 % 5 = 1 那么3就是2关于5的逆元 或者说2和3关于5互为逆元 这里3的效果如同1/2 所以叫数论倒数
那么我们就可以把除法转换为乘法(乘逆元)
求逆元的方法见代码实现
我会的是费马 扩展欧几里得 和线性计算
模板题目
题目描述
给定n,p求1~n中所有整数在模p意义下的乘法逆元。
输入输出格式
输入格式:
一行n,p
输出格式:
n行,第i行表示i在模p意义下的逆元。
输入输出样例
输入样例:
10 13
输出样例:
1
7
9
10
8
11
2
5
3
4
说明
输入保证p为质数。
代码实现
- 费小马定理
所以用上快速幂就行了
代码如下
ll n , p;
inline ll pow(ll a , ll b , ll p){
ll ans = 1;
while(b){
if(b & 1) ans = (ans*a)%p;
a = (a*a)%p;
b >>= 1;
}
return ans;
}
inline ll fermat(ll x , ll p){
return pow(x , p - 2 , p);
}
int main(){
n in; p in;
for(register int i=1;i<=n;i++)
printf("%d\n" , fermat(i , p));
return 0;
}
这个比较慢
- 扩展欧几里得
a∗x+b∗y=1
如果ab互质,有解
这个解的x就是a关于b的逆元
y就是b关于a的逆元
我们可以在等号两边同时取模a或者b证明
但是我真的看不大懂代码 背吧
代码如下
ll n , p;
ll x , y , d;
void exgcd(ll a , ll b , ll &x , ll &y , ll &d){
if(!b){
d = a; x = 1; y = 0;
}
else{
exgcd(b , a%b , y , x , d);
y -= x*(a/b);
}
}
inline ll inv(ll x , ll p){
exgcd(x , p , x , y , d);
return d == 1 ? (x%p + p)%p : -1;
}
int main(){
n in; p in;
for(register int i=1;i<=n;i++)
printf("%d\n" , inv(i , p));
}
这个比费马快一点 但也不是最快的
- 线性
推一下:
得到一个式子 我们可以递归搞
代码如下
ll n , p;
ll inv(ll x , ll p){
return x == 1 ? 1 : (p - p/x)*inv(p%x , p)%p;
}
int main(){
n in; p in;
for(register int i=1;i<=n;i++)
printf("%d\n" , inv(i , p));
}
但这样其实比费马还慢
所以我们建一个inv[size]来存结果 就不用多次递归浪费时间
代码如下
#include<iostream>
#include<cstdio>
#include<cctype>
using namespace std;
#define in = read();
typedef long long ll;
typedef unsigned int ui;
const ll size = 5000000 + 10000;\
ll n , p;
ll inv[size];
inline ll read(){
ll num = 0 , f = 1; char ch = getchar();
while(!isdigit(ch)){
if(ch == '-') f = -1;
ch = getchar();
}
while(isdigit(ch)){
num = num*10 + ch - '0';
ch = getchar();
}
return num*f;
}
int main(){
n in; p in;
inv[1] = 1; printf("1\n");
for(register int i=2;i<=n;i++){
inv[i] = (p - p/i)*inv[p%i]%p;
printf("%d\n" , inv[i]);
}
}
//COYG