以前写的几个数论模板,整整吧,反正数论这么弱貌似只会gcd的样子...
来,先来gcd
1.
#include<iostream> #include<cstdio> using namespace std; int gcd(int a,int b) { if(b==0) return a; else return gcd(b,a%b); } int lcm(int x,int y) { return (x*y)/gcd(x,y); } int main() { int a,b; scanf("%d%d",&a,&b); printf("%d %d",gcd(a,b),lcm(a,b)); return 0; }
gcd与lcm
扩展欧几里得
求解 x,y的方法的理解
设 a>b。
1,显然当 b=0,gcd(a,b)=a。此时 x=1,y=0;
2,a>b>0 时
设 ax1+ by1= gcd(a,b);
bx2+ (a mod b)y2= gcd(b,a mod b);
根据朴素的欧几里得原理有 gcd(a,b) = gcd(b,a mod b);
则:ax1+ by1= bx2+ (a mod b)y2;
即:ax1+ by1= bx2+ (a - [a / b] * b)y2=ay2+ bx2- [a / b] * by2;
也就是ax1+ by1 == ay2+ b(x2- [a / b] *y2);
根据恒等定理得:x1=y2; y1=x2- [a / b] *y2;
这样我们就得到了求解 x1,y1 的方法:x1,y1 的值基于 x2,y2.
2.ex-gcd
/* 扩展欧几里得 ax%b==1 -> ax-by==1 求不定方程的一组解 使x为最小正整数解 */ #include<iostream> #include<cstdio> #include<cstring> using namespace std; int x,y,gcd; int Extend(int a,int b) { if(b==0) { x=1;y=0; gcd=a; } else { Extend(b,a%b); int tmp=x; x=y; y=tmp-a/b*y; } } int main() { int a,b; scanf("%d%d",&a,&b); Extend(a,-b); x=x*(1/gcd); if(x<0)while(x<0)x=x+b; else while(x-b>0)x=x-b; printf("%d\n",x); return 0; }
codevs 同余方程
质因数分解
这个吗 一般算法的复杂度是够用的
当然如果筛素数的话会快 而且对于一个质数 可以直接返回
当然如果对n!每个数质因数分解的话那筛素数就快多了
当然我们还有别的方法 忘记叫什么名字了 咨询的数竞的孩子
n!里p的个数(p为素数)=n/p+n/(p*p)+n/(p*p*p)+n/(p*p*p*p)...(p*p*..)<=n
这样就快得多了 有时排列组合可以用到这个
3.
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; int n; int a[10000]; int main() { scanf("%d",&n); printf("%d=",n); int cnt=0; for (int i=2;i<=n;i++) { while (n%i==0&&n) { a[cnt++]=i; n/=i; } } for (int i=0;i<cnt-1;i++) { printf("%d*",a[i]); } printf("%d\n",a[cnt-1]); return 0; }
分解单个数
4.
#include<iostream> #include<cstdio> #define maxn 5000010 #define mod 100000007 #define ll long long using namespace std; ll n,prime[maxn/10],c[maxn/10],num,ans=1; bool f[maxn]; void Prime() { for(int i=2;i<=n;i++) { if(f[i]==0)prime[++num]=i; for(int j=1;j<=num;j++){ if(i*prime[j]>maxn-10)break; f[i*prime[j]]=1; if(i%prime[j]==0)break; } } } void Solve(){ for(int i=1;i<=num;i++){ ll P=prime[i]; if(P>n)break; while(n>=P){ c[i]+=n/P;P*=prime[i]; } if(c[i]) printf("%d ",c[i]); } } int main() { cin>>n; Prime();Solve(); return 0; }
阶乘分解
快速幂
5.
#include<iostream> #include<cstdio> #define mod 1000000007 using namespace std; int x,y; int main() { scanf("%d%d",&x,&y); int r=1; while(y) { if(y&1) r=(long long)r*x%mod;//y&1 pan duan zhe yi wei shi fou shi yi y>>=1;x=(long long)x*x%mod; } printf("%d",r%mod); return 0; }
快速幂取模
欧拉函数
定义:设n为正整数 不大于n的且与n互素的整数个数记为f(n)
求欧拉函数的方法
首先暴力好打 慢
f(n)=n*(1-1/p1)(1-1/p2)(1-1/p3)... pi为n的质因子
那就要分解质因数了 好办
应用嘛 还是比较好使的
求逆元
定义应用
a^(f(p))%p=1
6.
#include<iostream> #include<cstdio> #include<cstring> #define ll long long using namespace std; ll n,ans; ll init() { ll x=0;char s=getchar(); while(s<‘0‘||s>‘9‘)s=getchar(); while(s>=‘0‘&&s<=‘9‘){x=x*10+s-‘0‘;s=getchar();} return x; } int main() { while(1) { n=init(); if(n==0)break; ans=n; for(ll i=2;i*i<=n;i++) if(n%i==0) { while(n%i==0)n/=i; ans=ans/i*(i-1); } if(n>1)ans=ans/n*(n-1); printf("%ld\n",ans); } return 0; }
欧拉函数
素数
7.
#include<cstdio> #define maxn 22000 using namespace std; int l,r,cnt,num,prime[maxn]; bool f[maxn]; void Oprime(){ for(int i=2;i<=maxn;i++){ if(f[i]==0)prime[++num]=i; for(int j=1;j<=num;j++){ if(i*prime[j]>maxn)break; f[i*prime[j]]=1; if(i%prime[j]==0)break; } } } int main() { scanf("%d%d",&l,&r); Oprime(); for(int i=1;i<=num;i++){ if(prime[i]>=l&&prime[i]<=r)cnt++; if(prime[i]>r)break; } printf("%d\n",cnt); return 0; }
欧拉筛
素数判定
(1)费马小定理
复杂度(次数*logn)
若p为素数 则a^(p-1)%p=1
但是如果翻过来就不一定正确
优异类数叫做卡迈克尔数就是合数
但上面那个概率是很大的所以嘛
多生成几个a试试 一般出错的概率还是很小的
当然如果考试那天人品XX的话就怨不得谁了
8.
#include<iostream> #include<cstdio> #include<cstdlib> #include<cmath> #include<ctime> #define ll long long using namespace std; ll p,flag; ll mul(ll a,ll b) { a%=p;b%=p; ll r=0; while(b) { if(b&1) { b--;r+=a;r%=p; } a<<=1;a%=p;b>>=1; } return r%p; } ll qm(ll a,ll b) { ll r=1; while(b) { if(b&1) r=mul(r,a); b>>=1; a=mul(a,a); } return r; } int main() { ios::sync_with_stdio(false); cin>>p; if(p==1) { printf("No\n"); return 0; } if(p==2) { printf("Yes\n"); return 0; } srand(time(0)); for(int i=1;i<=15;i++) { int a=rand()%(p-1)+1; int x=qm(a,p-1); if(x!=1) { flag=1; break; } } if(flag) { printf("No"); return 0; } else { printf("Yes"); return 0; } return 0; }
费马小定理
(2)Miller Rabin(摘抄峰峰的,我不会)
复杂度(logn)好像还有个常熟 不过无伤大雅
再说说误判概率 经过k轮这玩意 将合数误判为素数的概率是(1/4)^k
素数误判为合数的概率是0 一般进行个10轮15轮就好了
要是这样的概率都给碰上了那就没啥可说的了....
下面说说算法过程
首先 2特判一下子 对于剩下的奇素数
有 fermat a^(p-1)%p=1
避免出错同时加速这个算法
另一个定理 若x&1,且x*x%p==1 则有x=1或x=-1(即x==p-1)
把p-1写成p-1=m*2^j 然后呢 j次测试 若不满足上面那个东西 直接肯定p不是prime
每次都乘回来 最后又得到了p-1 再来一次fermat就ok了
9.
#include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<ctime> #define ll long long using namespace std; ll p; ll mul(ll a,ll b){ a%=p;b%=p; ll r=0; while(b){ if(b&1){ b--;r+=a;r%=p; } a<<=1;a%=p;b>>=1; } return r%p; } ll qm(ll a,ll b){ ll r=1; while(b){ if(b&1)r=mul(r,a); b>>=1;a=mul(a,a); } return r; } bool Miller_Rabin(){ if(p<=2)return 1; if(p%2==0)return 0; ll x,y,m,k=0,T=15,a; m=p-1; while(m%2==0){ k++;m>>=1; } while(T--){ a=rand()%(p-1)+1; x=qm(a,m); for(ll i=1;i<=k;i++){ y=mul(x,x); if(y==1&&x!=1&&x!=p-1)return 0; x=y; } if(x!=1)return 0; } return 1; } int main() { cin>>p; if(Miller_Rabin())printf("Yes\n"); else printf("No\n"); return 0; }
Miller Rabin
表示还很弱很弱很弱,不会的还有很多很多很多.......