1. 程式人生 > 實用技巧 >乘法逆元

乘法逆元

乘法逆元1

題目連結

這裡有兩種方法求逆元其實我只會兩種qwq

在這裡放一個線性的式子:

\[inv_i = p-(p\div i)\times inv_{p\%i}\%p \]

原理我也不懂orz。

啊是的這是第一種方法。

另一種方法用到了費馬小定理,結論是:

在模數\(p\)為質數的情況下:

\[a^{-1}\equiv a^{p-2} (mod~~p) \]

不會證。

然後我們用快速冪求一下即可。

不過在這道題裡用第二種方法會T,必須用線性的求法。

乘法逆元2

題目連結

我們Tethys真的是太厲害啦!!!!

如果用線性求逆元的方法,我們需要求出從\(1\)\(a_{max}\)的所有逆元。

然而\(a_{max}\)上限為\(10^{9}\),這種做法顯然是不可行的。

有另一種求逆元的方法好像叫離線求逆元??

首先逆元是完全積性的,我們要知道\(a_i\)的字首積的逆元就是逆元的字首積。

所以我們在輸入的時候順便求一下字首積,然後有這麼兩個式子:

\[式子1:a_{i}^{-1}=pre_{i}^{-1}\times pre_{i-1}(1\leq i\leq n) \]

\[式子2:pre_{i-1}^{-1} = pre_{i}^{-1} \times a_i (1\leq i< n) \]

推式子:

式子是怎麼推的,這一步可以跳過,因為太簡單qwq

式子2:

第二個好推所以我們先推第二個 \(pre_{i-1}^{-1} = pre_{i}^{-1} \times a_i\)

首先逆元的字首積是這樣的:

\[pre_i^{-1} = a_{1}^{-1} \times a_{2}^{-1} \times ... \times a_{i}^{-1} \]

因為\(a^{-1} = \frac{1}{a}\)

所以可以變成這樣的形式:

\[pre_i^{-1} = \frac{1}{a_1\times a_{2} \times ... \times a_{i}} \]

顯然:

\[pre_{i-1}^{-1} = pre_i^{-1} \times a_i = \frac{1}{a_1\times a_{2} \times ... \times a_{i}} \times a_i = \frac{1}{a_1\times a_{2} \times ... \times a_{i-1}} \]

所以:

\[pre_{i-1}^{-1} = pre_{i}^{-1} \times a_i \]

式子1:

接下來推第一個式子\(a_{i}^{-1}=pre_{i}^{-1}\times pre_{i-1}\)

因為:

\[a_{i}^{-1} = \frac{1}{a_i} \]

因為字首積的逆元可以變成這樣:

\[pre_i^{-1} = \frac{1}{a_1\times a_{2} \times ... \times a_{i}} \]

把式子1展開相乘一下:

\[pre_i^{-1} \times pre_{i-1} = \frac{1}{a_1\times a_{2} \times ... \times a_{i}} \times a_1\times a_{2} \times ... \times a_{i-1} = \frac{1}{a_i} \]

所以:

\[a_{i}^{-1}=pre_{i}^{-1}\times pre_{i-1} \]

然後我們就推完了orz。

我們就可以根據這個兩個式子線性求出逆元的字首積,但是首先要\(log\)的求一下\(pre_n\)的逆元。

程式碼:

#include <bits/stdc++.h>
using namespace std;

template<typename temp>temp read(temp &x){
	x = 0;temp f = 1;char ch;
	while(!isdigit(ch = getchar())) (ch == '-') and (f = -1);
	for(x = ch^48; isdigit(ch = getchar()); x = (x<<1)+(x<<3)+(ch^48));
	return (x *= f);
}
template <typename temp, typename ...Args>void read(temp& a, Args& ...args){read(a), read(args...);}

const int maxn = 5e6+10;

long long n, p, k, ans, pre[maxn], inv_pre[maxn], a[maxn];

long long fast(long long x, long long y, long long p){
	long long ans = 1;
	while(y){
		if(y&1) ans = ans*x%p;
		x = x*x%p;
		y >>= 1;
	}
	return ans%p;
}

signed main(){
	read(n, p, k);pre[0] = 1;
	for(int i = 1; i <= n; i ++) pre[i] = pre[i-1]*read(a[i])%p;//求字首積
	inv_pre[n] = (fast(pre[n], p-2, p));//求出pre[n]的逆元
	for(int i = n; i >= 1; i --) inv_pre[i-1] = inv_pre[i]*a[i]%p;//求出逆元的字首積
	for(int i = n; i >= 1; i --) ans = (ans + inv_pre[i]*pre[i-1]%p)*k%p;//式子一求出a[i]的逆元
	printf("%lld", ans);
	return 0;
}