费马小定理与乘法逆元
RT,本文主要讲述费马小定理和乘法逆元的应用。
费马小定理
定义
对于质数
证明
,此时同余式两边都是0,成立 ,此时同余式两边都是0,成立
由于
是素数, ,故 。 引理1:
值互不相同 证明:反证法,设存在
, ,使得 移项可以得到
,此时说明 。但需要知道的是, ,故 不含有质因子 。 又因为
,就可以推出 。因为 ,所以 。 因为
是素数,这些数中不存在 的倍数。故假设不成立,原命题成立。 引理2:
的值不重不漏地取遍了 。 证明:由引理1可知,
的值互不相同,又因为对于任意整数 , ,且 所以
有 个不同的值,值域也是 。所以集合 与集合 构成一个双射,原命题成立。 由引理1和引理2可知:
,左式展开移项得到 。 因为
是素数,所以 。所以可以得到 。同余式同乘 即可得到 。
应用
Sum
题意:给定
求
分析:对于
根据隔板法,有
所以所求即为
对于这个式子,其等价于
组合证明:
从组合意义上,为对从
考虑任意一种拿球的方案,随便拿什么球,显然拿的球的个数
所以对于任意一种拿球的方案,都可以将其规为对从
综上所述,对从
代数证明:
由二项式定理可知:
由于
时间复杂度:
#define int long long
int a[1050050];
char s[1005050];
const int mod=1e9+7,p=1e9+6;
int power(int a,int b){
int ans=1;
while(b){
if(b&1)ans=ans*a%mod;
a=a*a%mod;
b>>=1;
}
return ans;
}
signed main(){
while(cin>>s){
int bit=strlen(s);
int n=0;
for(int i=0;i<bit;i++){
n=n*10+(s[i]-'0');n%=p;
}
cout<<power(2,n-1)<<"\n";
}
}
Ignatius's puzzle
题意简述:设
求最小的
解:
由算术基本定理可知,证明
先来看第一部分:
由于
此式对所有正整数
故可以得到
所以
类似地可以得到
由不定方程解的性质可知,若这两个方程同时有解,则最小正值
用裴蜀定理判断一下无解即可。
int main(){
while(cin>>k){
if(k%5==0||k%13==0)cout<<"no\n";
else {
for(int x=0;x<=65;x++){
if(x*k%5==2&&x*k%13==8){
cout<<x<<"\n";break;
}
}
}
}
return 0;
}
集合计数
这个题很妙。
先将
然后只需要考虑剩下的
设
则容易得到:
根据二项式反演定理可以得到:
问题转化为求
由于固定了
这些集合取交集这
故
用费马小定理给它把指数模一下即可。
所以最终答案为:
乘法逆元
定义
设
根据裴蜀定理,
意义
在模意义下,除法非常麻烦,因为模运算一般不会出现小数。
比如对于
则
求法
根据裴蜀定理,
扩展欧几里得
适用于存在乘法逆元的情况。
费马小定理
适用于
所以
黑科技
记
设我们需要求
设
然后设
证明显然。
递推
以上两种做法都可以在
则
证明:
设
则有
根据模运算的性质,
性质
- 倒数具有的性质逆元同样具有
- 所有合法的
,其模 的值都相等。也即设 是 逆元中的最小正值,则所有合法的逆元全部可以表示为
证明:根据扩展欧几里得法求逆元的过程和二元一次不定方程解的通项公式可知
应用
Sumdiv
首先因为9901是质数,所以可以考虑拆开。
记
设
括号拆开就可以得出所有的因数。
运用等比数列求和公式,我们知道:
需要注意的是,有可能
那么
#define int long long
int a,b,p=9901,n,c[100500],tot,cnt[100500],ans;
inline int power(int a,int b){
int ans=1,t=0;
while(b>=1){
++t;
if(b&1)ans=1ll*ans*a%p;
a=1ll*a*a%p;
b>>=1;
if(!b)break;
}
return ans;
}
inline void get(int x){
for(int i=2;i<=x;++i){
if(x%i==0){
c[++tot]=i;
while(x%i==0)x/=i,cnt[tot]++;
cnt[tot]*=b;cnt[tot]++;
}
}
}
void solve(){
get(a);ans=1;
for(int i=1;i<=tot;i++){
// cout<<c[i]<<" "<<cnt[i]<<"\n";
if((c[i]-1)%p==0){
ans*=cnt[i];
}
else ans=1ll*ans*(power(c[i],cnt[i])-1)%p*power(c[i]-1,p-2)%p;
}
}
signed main(){
ios::sync_with_stdio(false);
read(a),read(b);solve();
cout<<(ans%p+p)%p;//<<"\n"<<tot<<" "<<c[tot]<<" "<<cnt[tot];
}
排课表
考虑容斥。
显然总的组合方案数是
同理最后一节课选
所以总的方案数是:
字符串
感觉很妙。
仍然显然是容斥。
总的序列个数显然是
肯定有一个最小的位置
考虑限定这个位置,则剩下的
则不合法的方案数是
预处理阶乘的话,这玩意可以在
这就满足了吗?不,我们有更加优秀的做法。
从组合意义来讲,这个做法可以理解为,由于第
这里网上的题解是从双射的角度理解的,本质一样。
int n,m,jc[N];
inline int power(int a,int b){
int ans=1;
while(b){
if(b&1)ans=ans*a%p;
a=a*a%p;
b>>=1;
}
return ans;
}
int C(int n,int m){
return jc[n]*power(jc[m],p-2)%p*power(jc[n-m],p-2)%p;
}
signed main(){
ios::sync_with_stdio(false);
cin>>n>>m;
jc[1]=1;for(int i=2;i<=n+m;i++)jc[i]=jc[i-1]*i%p;
cout<<((C(n+m,m)-C(n+m,m-1))%p+p)%p;
}
黑科技-密码
这里提炼出一个通法:设
也即求高次同余方程
我们都知道,如果定
定
考虑对两边分别乘方
由费马小定理,可知
那么令
而由于
而对于本题,
#define int long long
int p,a=(1<<30)+3,b,t,s;
int power(int a,int b,int p){
int ans=1;
while(b){
if(b&1)ans=ans*a%p;
a=a*a%p;
b>>=1;
}
return ans;
}
int exgcd(int a,int b,int &x,int &y){
if(!b){
x=1,y=0;return b;
}
int d=exgcd(b,a%b,x,y);
int z=x;x=y,y=z-(a/b)*y;return d;
}
signed main(){
ios::sync_with_stdio(false);
int T;cin>>T;
while(T--){
cin>>s>>p;
int x,y;exgcd(a,p-1,x,y);
x=(x%(p-1)+p-1)%(p-1);
cout<<power(s,x,p)<<"\n";
}
}
卡农
组合神题,不过是不是应该绛紫了
看着是黑题还不敢做,读完题发现很水……
首先如果从二进制的角度来说,将一个集合表示为一个二进制数,则其对应了
题目里的限制条件有:
- 片段互异——不重复选数
- 每个音阶奏响次数为偶数——选出的所有数异或后可以得到0
- 集合有序——统计无序答案,去掉
即可
根据异或运算的性质,对于
则如果确定了前
确定前
每一个方案都唯一对应
其中不合法的情况有:
- 这些片段里面本身异或得到0的,也即本身合法的
- 这些片段里
的取值出现过的。
设
仅需解决第二个问题。
由于
再者,重复数的位置也在
同时思考,本身异或和为0,从异或序列里删掉两个数,异或和同样为0.
反过来看,我们可以看作两个相同的数插入了异或序列,一个只能在最末尾,一个可以在
则原本异或序列存在
所以方案数为
综上所述,可以得到:
显然
注意到
#define int long long
#define p 100000007
#define N 1000500
int m,A[N],f[N],n,num;
int power(int a,int b){
int ans=1;
while(b){
if(b&1)ans=a*ans%p;
a=a*a%p;
b>>=1;
}
return ans;
}
void init(){
cin>>n>>m;
A[0]=1;num=power(2,n)-1;
for(int i=1;i<=m;i++){
A[i]=A[i-1]*(num-i+1)%p;
A[i]=(A[i]%p+p)%p;
}
}
void solve(){
f[0]=1;
for(int i=1;i<m;i++){
f[i+1]=A[i]-f[i];
if(i>0)f[i+1]-=f[i-1]*i%p*(num-i+1)%p;
f[i+1]=(f[i+1]%p+p)%p;
}
for(int i=1;i<=m;i++)f[m]=f[m]*power(i,p-2)%p;
cout<<f[m]<<"\n";
}
signed main(){
ios::sync_with_stdio(false);
init();solve();
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!