数论
一个数约数个数=所有素因子的次数+1的乘积
举个例子就是48 = 2 ^ 4 * 3 ^ 1,所以它有(4 + 1) * (1 + 1) = 10个约数
****************************************************************************************************** 辗转相除 最大公约数 gcd(a,b): int gcd(int a,int b){ return b==0?a:gcd(b,a%b); } 如果b大于a则第一次递归转换a和b的值。 *************************************************************************************************** 最小公倍数 lcm(a,b): 利用gcd可以求出两个数a,b的最小公倍数数,有唯一分解定理得 a=p1^e1*p2^e2*...*pn^en; b=p1^f1*p2^f2*...*pn^fn; 则 gcd(a,b)=p1^min(e1,f1)*p2^min(e2,f2)*...*pn^min(en,fn); lcm(a,b)=p1^max(e1,f1)*p2^max(e2,f2)*...*pn^max(en,fn); 有gcd(a,b)*lcm(a,b)=a*b。如果把lcm写成a*b/gcd(a,b),可能会因此丢掉不少分数——a*b可能会溢出!正确的写法是先除后乘,即 a/gcd(a,b)*b。 ************************************************************************************************************* 素数帅选:对于不超过n的每一个非负整数p,删除2p,3p,4p,...,当处理完所有数之后,还没有被删除的就是素数,若用vis[i]表示i已被删除 memset(vis,0,sizeof(vis)); for(int i=2;i<=n;i++) for(int j=i*2;j<=n;j+=i) vis[j]=1; 改进: int m=sqrt(n+0.5); memset(vis,0,sizeof(vis)); for(int i=2;i<=m;i++) if(!vis[i]) for(int j=i*i;j<=n;j+=i) vis[j]=1; ******************************************************************************************************************* 扩展欧几里德算法: 找出一对整数(x,y),使得ax+by=gcd(a,b)。这里的x,y不一定是正数,也可能是负数或0。例如gcd(6,15)=3,6*3-15*1=3,其中x=3,y=-1。这个 方程还有其他解,如x=-2,y=1。 程序: void gcd(int a,int b,int &d,int &x,int &y){ if(!b){ d=a;x=1;y=0; }else{ gcd(b,a%b,d,y,x); y-=x*(a/b); } }
这个解的x就是a关于b的逆元
y就是b关于a的逆元
更一般的推论: 设a,b,c为任意整数。若方程ax+by=c的一组整数解为(x0,y0),则它的任意整数解都可以写成(x0+kB,y0-kA),其中A=a/gcd(a,b),B=b/gcd(a,b), (如果gcd(a,b)=0,意味着或b等于0,可以特殊判断)k取任意整数。 设a,b,c为任意整数,g=gcd(a,b),方程ax+by=g的一组解(x0,y0),则当c是g的倍数时ax+by=c的一组解是(x0c/g,y0c/g);当c不是g的整数倍时无整数解。 **************************************************************************************************************************** 同余与模运算: (a+b)mod n=((a mod n)+(b mod n))mod n (a-b)mod n=((a mod n)-(b mod n)+n)mod n ab mod n=(a mod n)(b mod n) mod n 减法中a mod n可能小于b mod n,需要在结果加上n。乘法中(a mod n)(b mod n)有时可能会溢出需要用long long,例如: int mul_mod(int a,int b,int n){ a%=n;b%=n; return (int)((long long)a*b%n); } 大整数取模 输入n,m,输出n mod m。 把大整数写成"自左向右"的形式:1234=((1*10+2)*10+3)*10+4,用前面的公式每步求模: scanf("%s%d",n,&m); int len=strlen(n); int ans=0; for(int i=0;i<len;i++) ans=(int)(((long long)ans*10+n[i]-'0')%m); printf("%d\n",ans); 幂取模 输入a,n,m,输出a^n mod m。 分治法对半递归计算: int pow_mod(int a,int n,int m){ if(n==0) return 1; int x=pow_mod(a,n/2,m); long long ans=(long long)x*x%m; if(n&1) ans=ans*a%m; return (int)ans; } 线性方程组 输入a,b,n,解方程ax≡b(mod n)。 a≡b(mod n)表示a和b关于模n同余,即a mod n=b mod n。ax≡b(mod n)的充要条件是a-b是n的整数倍。 这样原方程就可以理解为ax-b是n的整数倍,设这个倍数为y,则ax-b=ny,移项ax-ny=b,这就是前面的扩展欧几里德。 当b=1时,ax≡1(mod n),此时方程的解称为a关于模n的逆。当gcd(a,n)=1时,该方程有唯一解;否则无解。
(a^b)%p=((a%p)^(e(p)+b%e(p)))%p 其中e(p)表示P的欧拉函数值
**************************************************************************************************************
除法取余:
(a/b)%mod,当a,b非常大时无法计算。
费马小定理(Fermat Theory)是数论中的一个重要定理,其内容为: 假如p是质数,且gcd(a,p)=1,那么 a^(p-1)≡1(mod p)。即:假如a是整数,
p是质数,且a,p互质(即两者只有一个公约数1),那么a的(p-1)次方除以p的余数恒等于1。
可以用乘法的逆来解决,当然可知当成定理来用(a/b)%mod=(a*b^(mod-2))%mod,mod为素数
原理是费马小定理:a^(mod-1)%mod=1,又a^0%mod=1,a^(mod-1)%mod=a^0%mod,两边同时乘a^(-1),所以a^(-1)=a^(mod-2),推出a/b=a*b^(-1)=a*b^(mod-2)
//质因数分解模板 #include<iostream> #include<cstdio> #include<cstring> using namespace std; typedef long long ll; const int MAXN = 2000000; int prime[MAXN+1]; void getPrime()//求素数{ memset(prime,0,sizeof(prime)); for(int i = 2;i <= MAXN;i++){ if(!prime[i])prime[++prime[0]] = i; for(int j = 1;j <= prime[0] && prime[j] <= MAXN/i;j++){ prime[prime[j]*i] = 1; if(i % prime[j] == 0)break; } } } int factor[100][2];//factor[i][0]存素因子,factor[i][1]存素因子的个数 int fatCnt;//不重复的素因子个数 int getFactors(long long x) { fatCnt = 0; long long tmp = x; for(int i = 1; prime[i] <= tmp/prime[i];i++) { factor[fatCnt][1] = 0; if(tmp % prime[i] == 0 ) { factor[fatCnt][0] = prime[i]; while(tmp % prime[i] == 0) { factor[fatCnt][1] ++; tmp /= prime[i]; } fatCnt++; } } if(tmp != 1) { factor[fatCnt][0] = tmp; factor[fatCnt++][1] = 1; } return fatCnt; } int main() { getPrime(); int x; scanf("%d",&x); getFactors(x); return 0; }
//筛素数 const int MAXN = 1000000; int prime[MAXN+1]; void getPrime() { memset(prime,0,sizeof(prime)); for(int i = 2;i <= MAXN;i++) { if(!prime[i])prime[++prime[0]] = i; for(int j = 1;j <= prime[0] && prime[j] <= MAXN/i;j++) { prime[prime[j]*i] = 1; if(i % prime[j] == 0)break; } } }
//long long 数的素数判定+质因数分解 #include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<cstdlib> #include<ctime> #include<algorithm> using namespace std; typedef long long ll; const int S=20;//随机算法判定次数,S越大判错概率越小 //计算(a*b)%c;a,b,c<2^63 ll mult_mod(ll a,ll b,ll c){ a%=c; b%=c; ll ret=0; while(b){ if(b&1){ ret+=a; ret%=c; } a<<=1; if(a>=c) a%=c; b>>=1; } return ret; } //计算(x^n)%c ll pow_mod(ll x,ll n,ll c){ if(n==1) return x%c; x%=c; ll tmp=x; ll ret=1; while(n){ if(n&1) ret=mult_mod(ret,tmp,c); tmp=mult_mod(tmp,tmp,c); n>>=1; } return ret; } //以a为基,n-1=x*2^t a^(n-1)=1(mod n) 验证n是不是合数 //一定是合数返回true,不一定返回false bool check(ll a,ll n,ll x,ll t){ ll ret=pow_mod(a,x,n); ll last=ret; for(int i=1;i<=t;i++){ ret=mult_mod(ret,ret,n); if(ret==1&&last!=1&&last!=n-1) return true; last=ret; } if(ret!=1) return true; return false; } // Miller_Rabin()算法素数判定 //是素数返回true.(可能是伪素数,但概率极小) //合数返回false; bool Miller_Rabin(ll n){ if(n<2) return false; if(n==2) return true; if((n&1)==0) return false; ll x=n-1; ll t=0; while((x&1)==0){ x>>=1; t++; } for(int i=0;i<S;i++){ ll a=rand()%(n-1)+1; if(check(a,n,x,t)) return false; } return true; } ll gcd(ll a,ll b){ if(a==0) return 1; if(a<0) return gcd(-a,b); while(b){ ll t=a%b; a=b; b=t; } return a; } //Pollard_rho质因数分解算法 ll factor[1000];//质因数分解结果(无序的) int tot;//质因数的个数,数组下标从0开始 ll Pollard_rho(ll x,ll c){ ll i=1,k=2; ll x0=rand()%x; ll y=x0; while(1){ i++; x0=(mult_mod(x0,x0,x)+c)%x; ll d=gcd(y-x0,x); if(d!=1&&d!=x) return d; if(y==x0) return x; if(i==k){ y=x0; k+=k; } } } //对n进行素因子分解 void findfac(ll n){ tot=0; if(Miller_Rabin(n)){//素数 factor[tot++]=n; return; } ll p=n; while(p>=n) p=Pollard_rho(p,rand()%(n-1)+1); findfac(p); findfac(n/p); } int main() { return 0; }
//欧拉函数是积性函数即 phi(ab) = phi(a) * phi(b) (a与b互质); //1~n中与n互质的整数和为 n * phi(n) / 2 (n > 1); //sigma(d|n)phi(d) = n; //质数p和正整数k : phi(p^k) = p^k - p^(k - 1) = (p - 1) * p^(k - 1); // // #include<iostream> #include<cstring> using namespace std; typedef long long ll; const int MAXN=1000000; ll c[100]; int n,prime[MAXN+10],mu[MAXN+10],phi[MAXN+10],tot; bool check[MAXN+10]; //组合 //性质:c(n,k)+c(n,k+1)=c(n+1,k+1); //根据c(n,k+1)=c(n,k)*(n-k)/(k+1) 可以O(n)算出所有的。 void getC(){ c[0]=1; for(int i=0;i<n;i++) c[i+1]=c[i]*(n-i)/(i+1); } //扩展欧几里得 void kgcd(ll a,ll b,ll &d,ll &x,ll &y) { if(!b){ d=a; x=1;y=0; }else{ kgcd(b,a%b,d,y,x); y-=x*(a/b); } } //计算模m下a的逆,如果不存在返回-1; ll inv(ll a,ll m) { ll d,x,y; kgcd(a,n,d,x,y); return d==1 ? (x+n)%n : -1; } //线筛素数 void get_prime(){ memset(check,0,sizeof(check)); tot=0; for(int i=2;i<=MAXN;i++){ if(!check[i]) prime[tot++]=i; for(int j=0;j<tot;j++){ if(i*prime[j]>MAXN) break; check[i*prime[j]]=1; if(i%prime[j]==0) break; } } } //线筛欧拉函数 void get_phi(){ memset(check,0,sizeof(check)); phi[1]=1; tot=0; for(int i=2;i<=MAXN;i++){ if(!check[i]){ prime[tot++]=i; phi[i]=i-1; } for(int j=0;j<tot;j++){ if(i*prime[j]>MAXN) break; check[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); } } } } //线筛莫比乌斯函数 void get_mu(){ memset(check,0,sizeof(check)); mu[1]=1; tot=0; for(int i=2;i<=MAXN;i++){ if(!check[i]){ prime[tot++]=i; mu[i]=-1; } for(int j=0;j<tot;j++){ if(i*prime[j]>MAXN) break; check[i*prime[j]]=1; if(i%prime[j]==0){ mu[i*prime[j]]=0; break; }else{ mu[i*prime[j]]=-mu[i]; } } } } int main() { return 0; }