学习笔记--数论大杂烩 (看心情更新)
2016-02-14 20:42
495 查看
顺序有毒,请忽视。。。人傻,写的不好更忽视。。
一、费马小定理:
假如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\n",(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\n",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=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\n",ans); } return 0; }
十、Lucas定理(组合数取模):
A、B是非负整数,p是质数。AB写成p进制:A=a
a[n-1]…a[0],B=b
b[n-1]…b[0]。
则组合数C(A,B)与C(a
,b
)C(a[n-1],b[n-1])…*C(a[0],b[0]) modp同余
即:[b]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] *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]; }
相关文章推荐
- 学习笔记--数论大杂烩 (看心情更新)
- poj 1050 To the Max & uva 108
- 排序笔记_4(自上而下、自底而上的归并排序)
- eerTcirtemmyS.101
- 分治题目泛做(ME Second)
- Stanford 机器学习 Week3 作业 Logistic Regression
- 加密算法blowfish 多语言
- .h文件和.cpp文件
- 腰间盘突出的自我治疗和锻炼方法
- App上架可能会遇到的事情以及解决方案
- C++ new/delete操作符重载
- bestcoder #72 DIV2 Clarke and MST 最大按位与的生成树
- SpringMvc中Interceptor拦截器用法
- poj 1988 Cube Stacking
- 利用Excel进行简单的数据可视化
- 【C#】基础实例演练
- iOS内存管理
- oracle SQL_EXEC_ID分析
- spring表达式语言spel
- 框架编写之聊天框架认识(五)