【模板】乘法逆元
来源:互联网 发布:液压制图软件 编辑:程序博客网 时间:2024/05/22 05:15
- 基本介绍
- 模板题目
- 代码实现
基本介绍
先安利一个博客 非常好 镜外之主
数论倒数 又称逆元 (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;}
这个比较慢
- 扩展欧几里得
如果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
- 乘法逆元模板
- 乘法逆元 模板
- 乘法逆元模板
- [模板]乘法逆元
- 【模板】乘法逆元
- [模板]乘法逆元
- ACM 乘法逆元 模板
- [P3811][模板]乘法逆元
- 洛谷 P3811 【模板】乘法逆元
- 洛谷 P3811 【模板】乘法逆元
- 51nod 1256 乘法逆元 (模板)
- HDU 3037:Saving Beans Lucas模板题+乘法逆元
- Loj-110乘法逆元(线性模板题)
- codeforces 300C 乘法逆元 (乘法逆元模为素数的模板)
- 乘法逆元
- 乘法逆元
- 关于乘法逆元
- 乘法逆元
- 深信服算法工程师面试总结
- vector向量使用方法总结
- c++课
- javaspilt会遇到的异常java.util.regex.PatternSyntaxException: Syntax error in regexp pattern near index 1
- 2.leetCode595: Big Countries
- 【模板】乘法逆元
- Java基础语法
- effective cpp 读书笔记2
- 最长公共子序列 python
- 图片上传
- 文章标题
- 视频调色软件(colordirector 6) v6.0.2028中文免费版
- 数据库三级模式
- 使用playframework实现简单的增删改查