费马小定理与乘法逆元

RT,本文主要讲述费马小定理和乘法逆元的应用。

费马小定理

定义

对于质数\(p\),则对于任意自然数\(a\),有:

\[a^p\equiv a(\bmod p) \]

证明

  1. \(a=0\),此时同余式两边都是0,成立
  2. \(p\mid a\),此时同余式两边都是0,成立
  3. \(p\nmid a\)

由于\(p\)是素数,\(p\nmid a\),故\(\gcd(a,p)=1\)

引理1\(0\bmod p,a\bmod p,2a\bmod p……(p-1)a\bmod p\)值互不相同

证明:反证法,设存在\(i,j\)\(0\le i<j<p,i,j\in \mathbb{Z}\),使得\(ai\equiv aj(\bmod p)\)

移项可以得到\(a(j-i)\equiv 0(\bmod p)\),此时说明\(p|a(j-i)\)。但需要知道的是,\(\gcd(a,p)=1\),故\(a\)不含有质因子\(p\)

又因为\(p|a(j-i)\),就可以推出\(p|(j-i)\)。因为\(0\le i<j<p\),所以\(1\le j-i\le p-1\)

因为\(p\)是素数,这些数中不存在\(p\)的倍数。故假设不成立,原命题成立。

引理2\(a\bmod p,2a\bmod p……(p-1)a\bmod p\)的值不重不漏地取遍了\(1\sim p-1\)

证明:由引理1可知,\(a\bmod p,2a\bmod p……(p-1)a\bmod p\)的值互不相同,又因为对于任意整数\(k\)\(0\le ka\bmod p<p\),且\(0a\bmod p\equiv 0\)

所以\(a\bmod p,2a\bmod p……(p-1)a\bmod p\)\(p-1\)个不同的值,值域也是\([1,p-1]\)。所以集合\(\lbrace a\bmod p,2a\bmod p……(p-1)a\bmod p\rbrace\)与集合\(\lbrace 1,2,3,…p-1\rbrace\)构成一个双射,原命题成立。

由引理1和引理2可知:\(\prod_{i=0}^{p-1}ia\equiv (p-1)!(\bmod p)\),左式展开移项得到\(a^{p-1}(p-1)!\equiv (p-1)!(\bmod p)\)

因为\(p\)是素数,所以\(p\nmid(p-1)!\)。所以可以得到\((a^{p-1}-1)(p-1)!\equiv 0(\bmod p)\implies a^{p-1}\equiv 1(\bmod p)\)。同余式同乘\(a\)即可得到\(a^p\equiv a(\bmod p)\)

\(QED.\)

应用

Sum

题意:给定\(n\),设\(S(k)\)表示\(x_1+x_2+x_3+…+x_k=n\)的不定方程中,所有未知数都为正整数的解的个数。

\(\sum_{i=1}^nS(k)\bmod (10^9+7)\)

分析:对于\(S(k)\),可以视作有\(n\)个一样的球和\(k\)个不一样的盒子,要求不可以有空盒的方案个数。

根据隔板法,有\(n-1\)个空隙,需要选出\(k-1\)个空隙插入板子,方案数为\({n-1\choose k-1}\)

所以所求即为\(\sum_{i=1}^nS(k)=\sum_{i=1}^n{n-1\choose i-1}=\sum_{i=0}^{n-1}{n-1\choose i}\)

对于这个式子,其等价于\(2^{n-1}\),证明如下:

组合证明

从组合意义上,为对从\(n-1\)个球中拿出\(0,1,2,3……n-1\)个球的方案数总和。

考虑任意一种拿球的方案,随便拿什么球,显然拿的球的个数\(k\in[0,n-1]\)。则这个方案归属于拿出\(k\)个球的方案之一。

所以对于任意一种拿球的方案,都可以将其规为对从\(n-1\)个球中拿出\(0,1,2,3……n-1\)个球中的每一类。

综上所述,对从\(n-1\)个球中拿出\(0,1,2,3……n-1\)个球的方案与任意拿球的方案构成一个双射,且后者显然是\(2^{n-1}\)种方案(可选可不选)。

\(QED.\)

代数证明

二项式定理可知:

\[\sum_{i=0}^{n-1}{n-1\choose i}=\sum_{i=0}^{n-1}1^{i}1^{n-i-1}{n-1\choose i}=(1+1)^{n-1}=2^{n-1} \]

\(QED.\)

由于\(n\le 10^{1000000}\),所以可以对\(n\)当作字符串处理,边加边对\(10^9+6\)取模,最后快速幂即可。

时间复杂度:\(O(\log_{10}n)\)

#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

题意简述:设\(f(x)=5x^{13}+13x^5+kax\),其中\(a\)为未知数,\(k\)为给定的正整数。

求最小的\(a\)的取值,使得对于任意正整数\(x\)\(65|f(x)\)

解:

由算术基本定理可知,证明\(65|f(x)\)等价于求证\(5|f(x)\)\(13|f(x)\)

先来看第一部分:\(5|f(x)\)

由于\(5|5x^{13}\),所以\(f(x)\equiv 13x^5+kax\equiv 0(\bmod 5)\),因为\(5\)是质数,所以可以得到\(13x^5\equiv 13x(\bmod 5)\)。回带即可得到\((13+ak)x\equiv 0(\bmod 5)\)

此式对所有正整数\(x\)成立,所以可以假定\(\gcd(x,5)=1\),若不互质则此式显然成立。

故可以得到\(ak\equiv -13(\bmod 5)\implies ak\equiv 2\)

所以\(5|f(x)\)的充要条件就是\(ak\equiv 2(\bmod 5)\)
类似地可以得到\(13|f(x)\)的充要条件是\(ak\equiv 8(\bmod 13)\)

由不定方程解的性质可知,若这两个方程同时有解,则最小正值\(a\)一定不超过\(65\)。直接枚举即可。

用裴蜀定理判断一下无解即可。

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;
}

集合计数

这个题很妙。

先将\(k\)个元素选出来,也即这\(k\)个位置固定,方案数\({n\choose k}\)

然后只需要考虑剩下的\(n-k\)个元素。

\(g(i)\)表示交集元素数量至少为\(i\)个的情况。设\(f(i)\)表示刚好为\(i\)个的情况。所求为\(f(0)\)

则容易得到:

\[g(i)=\sum_{j=i}^{n-k}{n-k\choose j}f(j) \]

根据二项式反演定理可以得到:

\[f(0)=\sum_{i=0}^{n-k}(-1)^i{n-k\choose i}g(i) \]

问题转化为求\(g\)

由于固定了\(i\)个位置,选\(k\)的时候还固定了\(k\)个,所以还剩下\(2^{n-k-i}\)个集合。

这些集合取交集这\(i\)个元素都是合法的,但不能取空集。所以有方案数\(2^{2^{n-k-i}}-1\)

\(g(i)=2^{2^{n-k-i}}-1\)

用费马小定理给它把指数模一下即可。

所以最终答案为:

\[f(0)=\sum_{i=0}^{n-k}(-1)^i\left(2^{2^{n-k-i}}-1\right){n-k\choose i} \]

乘法逆元

定义

\(ax\equiv 1(\bmod p)\),则\(x\)\(a\)在模\(p\)意义下的逆元。

根据裴蜀定理,\(a\)存在模\(p\)意义下的逆元的充要条件是\(\gcd(a,p)=1\).。做题时需要注意这个点。

意义

在模意义下,除法非常麻烦,因为模运算一般不会出现小数。

比如对于\(\frac{m}{a}\bmod p\),不可能对小数进行取模,所以就有了乘法逆元。设\(ax\equiv 1(\bmod p)\)

\(\frac{m}{a}\bmod p=m·\frac{1}{a}\bmod p\)。又因为\(a·\frac{1}{a}\equiv ax\equiv 1(\bmod p)\)。所以\(\frac{m}{a}\equiv mx(\bmod p)\)

求法

根据裴蜀定理,\(a\)存在模\(p\)意义下的逆元的充要条件是\(\gcd(a,p)=1\).。做题时需要注意这个点。

扩展欧几里得

适用于存在乘法逆元的情况。\(ax\equiv 1(\bmod p)\)本身就是一个线性同余方程,食用扩展欧几里得进行求解即可。

费马小定理

适用于\(p\)是素数的情况。我们都知道\(a^p\equiv a\implies a·a^{p-2}\equiv 1(\bmod p)\)

所以\(a^{p-2}\)就是\(a\)在模\(p\)意义下的一个逆元。

黑科技

\(inv_a\)表示\(a\)在模\(p\)意义下的逆元。
设我们需要求\(a_1,a_2…a_n\)的逆元,这个黑科技可以做到\(O(n+\log p)\)

\(s_i=\prod_{j=1}^ia_i\),设\(H=inv_{s_n}\),这一步是\(O(n+\log p)\)的。

然后设\(h_i=H\prod_{k=i+1}^na_k\),则\(inv_{a_i}=h_i·s_{i-1}\)

证明显然。

递推

以上两种做法都可以在\(O(\log n)\)的时间内求出单个数的乘法逆元,但这远远不够。有时候我们需要求出\(1\sim n\)每一个数的乘法逆元,这时候就需要递推法了。

\(inv_a\equiv (p-\left\lfloor\frac{p}{a}\right\rfloor)·inv_{p\bmod a}\)

证明:

\(t=\left\lfloor\frac{p}{a}\right\rfloor,p=at+r(0\le r<a)\)

则有\(at+r\equiv 0(\bmod p)\implies at\equiv -r(\bmod p)\)。两边同时乘\(inv_a\)得到\(t\equiv -r·inv_a(\bmod p)\)。移项得到\(inv_a\equiv -t·inv_r(\bmod p)\)。负数化正得到\(inv_a\equiv p-t·inv_r\)

根据模运算的性质,\(r=p\mod i\)。带入即可得到:

\[inv_a\equiv (p-\left\lfloor\frac{p}{a}\right\rfloor)·inv_{p\bmod a} \]

性质

  1. 倒数具有的性质逆元同样具有
  2. 所有合法的\(inv_a\),其模\(p\)的值都相等。也即设\(inv_a\)\(a\)逆元中的最小正值,则所有合法的逆元全部可以表示为\(kp+inv_a,k\in \mathbb{Z},0\le inv_a< p\)

证明:根据扩展欧几里得法求逆元的过程和二元一次不定方程解的通项公式可知

应用

Sumdiv

首先因为9901是质数,所以可以考虑拆开。

\(f(A)\)\(A\)的约数和。

\(A\)质因数分解后为\(p_1^{c_1}p_2^{c_2}…p_m^{c_m}\),则对于这些数的任意组合都是\(A\)的约数,根据乘法原理可以得到:

\[f(A)=\prod_{i=1}^m\left(\sum_{k=1}^{c_i}p_i^k\right) \]

括号拆开就可以得出所有的因数。

运用等比数列求和公式,我们知道:

\[f(A)=\prod_{i=1}^m\frac{p_i^{c_i+1}-1}{p_i-1} \]

需要注意的是,有可能\(9901|(p_i-1)\),此时没有逆元,但容易知道\(p_i\)\(p\)后的贡献为\(c_i+1\),证明考虑对于每一个\(p_i^k\)拆开为\((x9901+1)^k\),其模\(9901\)的值显然为1.

那么\(f(A^B)\)就是令所有的\(c'_i=c_iB\)即可。

#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];
}

排课表

考虑容斥。

显然总的组合方案数是\({n\choose m}\),第一节课选\(a\)节课中的其中一个的方案数是\(a{n-1\choose m-1}\)

同理最后一节课选\(b\)的方案数是\(b{n-1\choose m-1}\)。然后第一节课选\(a\)中一个,最后一节课选\(b\)中一个,方案数是\(ab{n-2\choose m-2}\)

所以总的方案数是:

\[{n\choose m}-(a+b){n-1\choose m-1}+ab{n-2\choose m-2}=\frac{n!-(a+b)(n-1)!+ab(n-2)!}{(n-m)!} \]

字符串

感觉很妙。

仍然显然是容斥。

总的序列个数显然是\({n+m\choose m}\),考虑一个不合法序列所需要的特征:

肯定有一个最小的位置\(k=2x+1\),使得\([1,k]\)不合法。

考虑限定这个位置,则剩下的\([k+1,n+m]\)随便取,方案数是\({n+m-2x-1\choose m-x-1}\),前面的序列的方案数是\({k\choose x+1}\)。显然\(x\)可以取\(1\sim\left\lfloor\frac{n+m-1}{2}\right\rfloor\)。对其进行求和,设\(t=\left\lfloor\frac{n+m-1}{2}\right\rfloor\)

则不合法的方案数是\(\sum_{i=0}^t{n+m-2i-1\choose m-i-1}{2i\choose i}\)

预处理阶乘的话,这玩意可以在\(O(n)\)的复杂度内求出。

这就满足了吗?不,我们有更加优秀的做法。

从组合意义来讲,这个做法可以理解为,由于第\(k\)个位置一定为0,所以还剩下\(m-1\)个0可以拿,直接从\(n+m\)个数里挑\(m-1\)个0,剩下的一个随波逐流,也正好可以归为\(k\)的某一个取值中的某一类。由于不合法序列存在且仅存在一个位置,所以其实这个剩下的0是确定的,是\(m\)个零中的某一个,故方案数\(m·\frac{1}{m}{n+m\choose m-1}={n+m\choose m-1}\)

这里网上的题解是从双射的角度理解的,本质一样。

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;	
}

黑科技-密码

这里提炼出一个通法:设\(p\)是素数,\(\gcd(a,p-1)=1\)\(s\)为给定的值,定指数类高次同余方程的解法

也即求高次同余方程\(s\equiv t^a(\bmod p)\)的值\(t\)

我们都知道,如果定\(t\)不定\(a\),则可以通过BSGS算法根号内求解,

\(a\)不定\(t\)呢?

考虑对两边分别乘方\(k\)次,化为\(s^k\equiv t^{ak}(\bmod p)\)

由费马小定理,可知\(s^k\equiv t^{ak \bmod (p-1)}(\bmod p)\)

那么令\(ak\equiv 1(\bmod (p-1))\),此时\(s^k\)就是一个解。

而由于\(\gcd(a,p-1)=1\),所以不定方程\(ak+b(p-1)=1\)有无穷组解,求出\(k\)的最小正值解即可。

而对于本题,\(a=2^{30}+3\)是一个质数,满足条件,直接求即可。

#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,2^n-1]\)中的所有整数。

题目里的限制条件有:

  1. 片段互异——不重复选数
  2. 每个音阶奏响次数为偶数——选出的所有数异或后可以得到0
  3. 集合有序——统计无序答案,去掉\(m!\)即可

根据异或运算的性质,对于\(a_1\oplus a_2\oplus…a_k\oplus x=0\implies x=a_1\oplus a_2…a_k\)

则如果确定了前\(i\)个片段,第\(i+1\)个也随之确定,且唯一。

确定前\(i\)个片段的方案数是——\(A_{2^n-1}^i\)

每一个方案都唯一对应\(i+1\)的取值。

其中不合法的情况有:

  1. 这些片段里面本身异或得到0的,也即本身合法的
  2. 这些片段里\(i+1\)的取值出现过的。

\(f_i\)表示选出\(i\)个数的合法方案数,则第一个问题方案数就是\(f_i\)

仅需解决第二个问题。

由于\(i+1\)是确定的,那么重复的数也是确定的,所以不确定的位置只有\(i-1\)

再者,重复数的位置也在\(1\sim i\)之间的某个位置,进行枚举有\(i\)种可能性。

同时思考,本身异或和为0,从异或序列里删掉两个数,异或和同样为0.

反过来看,我们可以看作两个相同的数插入了异或序列,一个只能在最末尾,一个可以在\(1\sim i\)的任意位置。

则原本异或序列存在\(f_{i-1}\)个,插入的数可以有\(2^n-1-(i-1)\)不同取法,每种取法有\(i\)种插入方法。

所以方案数为\(f_{i-1}(2^n-i)i\)

综上所述,可以得到:

\[f_{i+1}=A_{2^n-1}^i-f_i-f_{i-1}(2^n-i)i \]

显然\(f_0=1,f_1=0\),因为不能不选。最终答案是\(f_n·inv_{n!}\bmod (10^8+7)\)

注意到\(A_{2^n-1}^i\)中底数非常大,但由于其固定,所以可以预处理。

#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;
}
posted @ 2023-03-05 07:38  spdarkle  阅读(99)  评论(0编辑  收藏  举报