顺序有毒,请忽视。。。人傻,写的不好更忽视。。
一、费马小定理:
假如p是质数,且(a,p)=1,那么 a^(p-1)≡1(mod p)。即:假如a是整数,p是质数,且a,p互质(即两者只有一个公约数1),那么a的(p-1)次方除以p的余数恒等于1。
证明略去,跟着这个网站证非常的舒服:http://www.xieguofang.cn/Maths/Number_Theory/Fermat%27s_Little_Theorem_1.htm#s2
关于费马小定理的运用吧:
在涉及取模运算的计算中,如果有除法,不能直接除以一个数,而应该变成乘以它的乘法逆元。
当我们除以一个数n时,也就是乘上1/n,若x是1/n关于模N的逆元,则x=1/n (mod N),即 x*n=1(mod N)。由于我们做题时N常常为1000000007,而1000000007是个素数,所以它满足了费马小定理,而满足费马小定理说明解唯一,所以我们可以直接得出x*n=n^(N-1)。那么x=n^(N-2),即为1/n关于模N的乘法逆元。(总之就是求逆元)
可以用来检验一个数,是否是素数(其实然并卵)
1、算出大数的余数 2、判断某种形状的素数个数 3、证明已知数是素数膜的原根 4、判断同余方程有解的必要条件。。
可以用费马小定理来降幂
二、欧拉函数φ:
欧拉函数的应用很多,尤其是上述加粗公式,很多问题在化简中所必需用到的公式,需要牢记。
线性筛求欧拉函数模板:(利用积性函数的性质)
long long prime[maxn],phi[maxn];
bool flag[maxn]={0};
int n;
void get_phi()
{
memset(flag,0,sizeof(flag));
flag[1]=1;
int cnt=0;
for (int i=2; i<=maxn; i++)
{
if (!flag[i])
prime[cnt++]=i,phi[i]=i-1;
for (int j=0; j<cnt && i*prime[j]<maxn; j++)
{
flag[i*prime[j]]=1;
if (i%prime[j]==0)
{
phi[i*prime[j]]=phi[i]*prime[j];
break;
}
else
phi[i*prime[j]]=phi[i]*(prime[j]-1);
}
}
}
求单个欧拉函数
int euler_phi(int n)//求单个欧拉函数
{
int m=(int)sqrt(n+0.5);
int i,ans=n;
for(i=2;i<=m;i++)
if(n%i==0)
{
ans=ans/i*(i-1);
while(n%i==0)n/=i;
}
if(n>1)ans=ans/n*(n-1);
return ans;
}
三、高斯函数:
即取整函数,在推公式中用处极大,变成中有现成的。。。
即下取整;
反之上取整;
四、线性同余方程(拓展欧几里得算法):
a,b均为已知
于是乎,解此类同余方程可以用到拓展欧几里得算法EXGCD:
#include<cstdio>
using namespace std;
void exgcd(long long a,long long b,long long &x,long long &y)
{
if(b==0)
{
x=1;y=0;return;
}
exgcd(b,a%b,x,y);
long long t=x;
x=y;
y=t-(a/b)*y;
}
int main()
{
long long a,b;
scanf("%lld%lld",&a,&b);
long long x,y;
exgcd(a,b,x,y);
printf("%lld",(x+b)%b);
return 0;
}
五、快速幂:
long long quick_pow(int a,int b,int p)//返回a^b%p的值
{
long long re=1;
long long tmp=a%p;
while (b)
{
if (b&1)
re=(re*tmp)%p;
tmp=(tmp*tmp)%p;
b=b>>1;
}
return re;
}
六、逆元:
除以一个数,等于乘上一个数的逆元,所以形如a/b%p就等价于a*b^-1(b的逆元)%p;(谁让除法有毒呢)
拓展欧几里得法,求逆元:(基本万能喽)
#include<iostream>
#include<cstdio>
using namespace std;
int n,mod,x,y;
void exgcd(int a, int b)
{
if(b==0)
{
x = 1;
y = 0;
return;
}
exgcd(b, a%b);
int t = x;
x = y;
y = t - a/b*y;
}
int main()
{
//n在模mod意义下的逆元
scanf("%d%d",&n,&mod);
exgcd(n,mod);
printf("%d
",(x%mod+mod)%mod);
return 0;
}
安利两种线性求逆元:
memset(flag,0,sizeof(flag));
flag[1]=1; inv[1]=1;jc[1]=1;//inv为逆元,jc为阶乘
int cnt=0;
for (int i=2; i<=maxn; i++)
{
jc[i]=jc[i-1]*i%p;
if (!flag[i])
prime[++cnt]=i,inv[i]=quick_pow(i,p-2,p);
for (int j=1; j<=cnt && i*prime[j]<=maxn; j++)
{
flag[i*prime[j]]=1;
inv[i*prime[j]]=inv[i]*inv[prime[j]]%p;
if (i%prime[j]==0) break;
}
}
inv[1]=1;
for (int i=2; i<=maxn&&i<p; i++)
inv[i]=(p-p/i)*inv[p%i]%p;
七、中国剩余定理:
若要问我:你对“中国剩余定理”的态度是怎样的呢?回答只有两个字:“敬畏”。
int china(int *a,int *m,int n)
{
int M=1,ans=0,mi,i,x,y;
for(i=0;i<n;i++) M*=m[i];
for(i=0;i<n;i++)
{
mi=M/m[i];
exgcd(m[i],mi,x,y);
ans=(ans+mi*y*a[i])%M;
}
return (ans%M+M)%M;
}
中国剩余定理
#include <iostream>
#include<cstdio>
using namespace std;
int Extended_Euclid(int a,int b,int &x,int &y) //扩展欧几里得算法
{
int d;
if(b==0)
{
x=1;y=0;
return a;
}
d=Extended_Euclid(b,a%b,y,x);
y-=a/b*x;
return d;
}
int Chinese_Remainder(int a[],int w[],int len) //中国剩余定理 a[]存放余数 w[]存放两两互质的数
{
int i,d,x,y,m,n,ret;
ret=0;
n=1;
for (i=0;i<len;i++)
n*=w[i];
for (i=0;i<len;i++)
{
m=n/w[i];
d=Extended_Euclid(w[i],m,x,y);
ret=(ret+y*m*a[i])%n;
}
return (n+ret%n)%n;
}
int main()
{
int n,i;
int w[15],b[15],cs[15];
while (scanf("%d",&n),n)
{
for (i=0;i<n;i++)
{
scanf("%d%d%d",&cs[i],&w[i],&b[i]);
}
printf("%d
",Chinese_Remainder(b,w,n));
}
return 0;
}
八、质因数分解:
对于i,从2到sqrt((double)m),如果m%i==0,则i是m的一个质因数,
然后m/i=m,i=2,重新判断,直至最后,剩下的那个数同样是一个质因数,要加上的.
int zhi(int n,int *x){ int count=0; int i=2;
while(i<=(int)sqrt((double)n)){ if(n%i==0){
x[count++]=i; n=n/i; }else{ i++; } }
x[count++]=n; return count;
}//返回质因数的个数,数组x是所有的因子
//n是要分解的数,pn是100范围内质数的个数,prim是100范围内的所有质数,q存储分解出来的质数相应的int zhi_num(int n,int pn,int *prim,int *q) {
for (int j=0;j<pn&&prim[j]<=n;j++) {
if (n%prim[j]==0) {
__int64 temp=0;
while (n%prim[j]==0) {
temp++;//相应的prim[j]的个数 n/=prim[j]; }
q[j]+=temp; } } }
prim[25]={2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97}; int q[25]={0}; int pn=25;
zhi_num(20,pn,prim,q);
九、BSGS(Baby Steps Gaint Steps):
安利一下算法流程:(不同于一般的BSGS)
首先要求 a^x=b (mod c)
假定x=im-j (m=ceil(sqrt(n)))
那么进行变换:
a^im = b*a^j (mod c)
枚举b*a^j mod p的值存入哈希表(map)。
再枚举a^im的值,从哈希表里找,如果能找到一样的答案就是i*m-j
具体模板详见题目;
EXBSGS:拓展到c不为质数的情况
模板:
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<map>
#include<cmath>
using namespace std;
long long gcd(long long a,long long b)
{
if (b==0) return a; else return gcd(b,a%b);
}
long long quick_pow(long long x,long long y,long long p)
{
long long re=1; x%=p; y%=p;
for (long long i=y; i; i>>=1,x=x*x%p)
if (i&1) re=re*x%p;
return re;
}
long long bsgs(long long a,long long b,long long p){
if(a%=p,b%=p,b==1)return 0;
long long t=1;long long f,g,delta=0,m=sqrt(p)+1,i;
for(g=gcd(a,p);g!=1;g=gcd(a,p)){
if(b%g)return -1;
b/=g,p/=g,t=t*(a/g)%p,delta++;
if(b==t)return delta;
}
map<long long,long long>hash;
for(i=0;i<m;i++,b=b*a%p)hash[b]=i;
for(i=1,f=quick_pow(a,m,p);i<=m+1;i++)
if(t=t*f%p,hash.count(t))return i*m-hash[t]+delta;
return -1;
}
int main()
{
long long a,b,p;
while (cin>>a>>b>>p)
{
//if (a==0 && b==0 && p==0) break;
long long ans=bsgs(a,b,p);
if (ans==-1) puts("No Solution");
else printf("%lld
",ans);
}
return 0;
}
十、Lucas定理(组合数取模):
A、B是非负整数,p是质数。AB写成p进制:A=a[n]a[n-1]…a[0],B=b[n]b[n-1]…b[0]。
则组合数C(A,B)与C(a[n],b[n])C(a[n-1],b[n-1])…*C(a[0],b[0]) modp同余
即:Lucas(n,m,p)=c(n%p,m%p)*Lucas(n/p,m/p,p)
证明略,可以看这里:http://blog.csdn.net/pi9nc/article/details/9615359
实现:
int quick_pow(long long a,int b,int p)//快速幂(算逆元)
{
int ans=1;
for(int i=b;i;i>>=1,a=(a*a)%p)
if(i&1)ans=(ans*a)%p;
return ans;
}
void cs()//初始出阶乘
{
jc[1][0]=jc[2][0]=jc[3][0]=jc[0][0]=1;//第一维无意义(因题目分成的几个模质数)
for (int i=0; i<4; i++)
for (int j=1; j<=pp[i]; j++)
jc[i][j]=(jc[i][j-1]*j)%pp[i];
}
int C(int n,int m,int p)//组合数
{
if (n<m) return 0;
return jc[p][n]*quick_pow(jc[p][m]*jc[p][n-m],pp[p]-2,pp[p])%pp[p];
}
int lucas(int n,int m,int p)//lucas
{
if (m==0) return 1;
return C(n%pp[p],m%pp[p],p)*lucas(n/pp[p],m/pp[p],p)%pp[p];
}