概念引入
快速幂求乘法逆元(费马小定理)
费马小定理概念:
公式原式为:
a
∗
x
≡
1
(
m
o
d
p
)
公式原式为:a∗x≡1(mod p)
公式原式为:a∗x≡1(modp)
带入可得
a
∗
x
≡
a
p
−
1
(
m
o
d
p
)
带入可得 a*x≡a^{p-1}(mod p)
带入可得a∗x≡ap−1(modp)
x
≡
a
p
−
2
(
m
o
d
p
)
x≡a^{p-2}(mod p)
x≡ap−2(modp)
所以我们可以用快速幂来算出x的值,这个数就是 a 的逆元了
洛谷的例题:https://www.luogu.com.cn/problem/U381899
编程
int qmi(int a,int b,int p){
int ans=1;
while(b){
if(b & 1) ans=ans*a%p;
b>>=1;
a=a*a%p;
}
return ans;
}
void solve(){
int n,p;
cin >> n >> p;
cout << qmi(n,p-2,p) << endl;
return ;
}
线性算法
例题来源:https://www.luogu.com.cn/problem/P3811
乘法逆元的线性算法主要就是公式的推导,这里直接引入洛谷大佬的推导过程:
编程
void solve(){
int n,p;
cin >> n >> p;
inv[1]=1;
cout << inv[1] << endl;
for(int i=2;i<=n;++i){
inv[i]=(p - p / i) * inv[p % i] % p;
cout << inv[i] << endl;
}
return ;
}
应用:随机栈
题目来源:2024CCPC全国邀请赛(郑州):https://codeforces.com/gym/105158
题目描述
有2n次操作,每次操作有两种情况:
-1:从集合里面取出一个数
非-1:将这个数存入集合里面
求最后取出来的数组满足递增(小于等于后一项)的概率,然后对这个概率进行取模处理。
解题思路
首先我们可以开一个最小堆模拟集合,因为每次取出的数必须是集合里面最小的数,否则就不满足题意,堆中的元素总数为分母,当前队头的最小元素的数量即为分子,该数量可以用map来记录,计算不同分数的积并进行取模,最后将分母进行快速幂的逆元,然后乘上分子在取模一次即可。
编程
#include<bits/stdc++.h>
#define int long long
#define endl "\n"
#define fi first
#define se second
#define PII pair<int,int>
using namespace std;
const int N=4e5+5;
int a[N];
int cnt[N];
int mod=998244353;
priority_queue<int,vector<int>,greater<int>> q;
int qmi(int a,int b){
int ans=1;
while(b){
if(b & 1) ans=ans*a%mod;
b>>=1;
a=a*a%mod;
}
return ans;
}
void solve(){
int n;cin >> n;
int fz=1,fm=1;
int maxn=0;
for(int i=1;i<=2*n;++i){
int x;cin >> x;
if(x!=-1){
q.push(x),cnt[x]++;
if(x<maxn){
cout << 0 << endl;
return ;
}
}
else{
fz=(fz*cnt[q.top()])%mod;
fm=(fm*q.size())%mod;
maxn=q.top();
cnt[q.top()]--,q.pop();
}
}
fm=qmi(fm,mod-2)%mod;
int ans=fm*fz%mod;
cout << ans << endl;
return ;
}
signed main(){
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
int t=1;
//cin >> t;
while(t--) solve();
// cout << fixed;//强制以小数形式显示
// cout << setprecision(n); //保留n位小数
return 0;
}