这里总结了一些基础的数学概念。
整除:
一个数可以被另一个数整除是数论中的一个关键概念。d|a(d整除a,a除以d)的含义是存在某个整数k,使得a=kd。称a是d的倍数,d是a的约数(因数)。
带余数除法:
a、b为两个正整数,且b≠,0,则存在唯一整数q和r,使得a=qb+r,0<=r
关于d|b的一些推论
① d|a且d|b,则d|(a+b)且d|(a-b)
② d|a且d|b,则d|(ax+by)
③ a|b且b|a,则a=±b
最大公约数:
一般用gcd(a,b)来表示a和b的最大公约数。并且对于任意非负整数a和任意正整数b,有gcd(a,b)=gcd(b,a mod b),证明略。
用代码求最大公约数:
int gcd(int a,int b)//保证a>b
{
return b?gcd(b,a%b):a;
}
关于最大公约数的一些推论:
① 对于ax+by(x,y∈Z)的线性组合集,gcd(a,b)是这个集合中的最小正整数。
② d|a且d|b,则d|gcd(a,b)
③ 对于n,a,b∈N+,如果n|ab,且gcd(a,n)=1,则n|b
互质数:
对于两个整数a和b,如果gcd(a,b)=1,则称a和b为互质数。等价于ax+by=1
唯一因子分解定理:
合数a仅能以一种方式写成如下乘积形式a=p1^e1*p2^e2*p3^e3……*pn^en,其中pi为素数,ei均为正整数。
扩展gcd算法:
该算法用来求解形如ax+by=c的通解。我们知道了ax+by=gcd(a,b)的方程肯定存在解,只要我们找到一组特殊的解x0和y0,便可以通过x=x0+(b/gcd(a,b))*t,y=y0-(a/gcd(a,b))*t。
考虑当前状态和下一个状态,有ax+by=gcd(a,b)=gcd(b,a%b)=bx1+(a%b)y1=bx1+(a-(a/b)*b)y1=ay1+b(x1-a/b*y1)。
前后式子对比,可得x=y1,y=x1-a/b*y1;
int e_gcd(int a,int b,int &x,int&y)
{
if(b==0){x=1;y=0;return a;}
int ans=e_gcd(b,a%b,x,y);
int temp=x;
x=y;
y=temp-a/b*y;
return ans;
}
扩展gcd的三个应用:
① 求解不定方程
形如ax+by=c的不定方程,若c mod gcd(a,b)=0,则该方程存在整数解,否则不存在。
② 求解模线性方程(线性同余方程)
同余方程ax≡b(mod n)(即ax和b关于n同余 b)对于未知数x有解,当且仅当gcd(a,n)|b。且当方程有解时,方程有gcd(a,n)个解。
对于该同余方程,相当于求解ax+ny=b。
证明:设d=gcd(a,n),则ax+ny=d.如果d|b,则ax0+ny0=d,同乘b/d得ax0*b/d+ny0*b/d=b。所以x=x0*b/d,y=y0*b/d为ax+ny=d的一个解,即x=x0*b/d为ax≡b(mod n)的解。且方程的 d个解分别为 xi= (x0+ i* (n/ d ))mod n {i= 0... d-1}。
设ans=x*(b/d),s=n/d;方程ax≡b (mod n)的最小整数解为:(ans%s+s)%s;
boolmodular_linear_equation(int a,int b,int n)
{
int x,y,x0,i;
int d=exgcd(a,n,x,y);
if(b%d)
return false;
x0=x*(b/d)%n; //特解
for(i=1;i
③ 求解模的逆元
当ax≡b(mod n)的gcd(a,n)=1时,方程只有唯一解。如果b=1,则x是a对模n的乘法逆元。即对于任意ax≡1(mod n),如果gcd(a,n)=1,那么方程ax≡1(mod n)对模n有唯一解,否则无解。解方程组ax+ny=1,求得x即为逆元。
int cal(int a,int m)
{
intx,y;
intgcd=e_gcd(a,m,x,y);
if(1%gcd!=0)return -1;
x*=1/gcd;
m=abs(m);
inans=x%m;
if(ans<0)ans+=m;
returnans;
}
中国剩余定理:
有物不知其数,三三数之剩二,五五数之剩三,七七数之剩二。问物几何?翻译过来就是有一个数x,x%3=2,x%5=3,x%7=2,求x。中国剩余定理便用来解决这一类问题。
(设m1,m2,···,mk两两互素)
x=a1(mod m1);
x=a2(mod m2);
···
x=ak(mod mk);
其在M=m1*m2*···*mk;中有唯一整数解。
记Mi=M/mi;因为gcd(Mi,mi)=1,故有两整数pi,qi满足Mi*pi+mi*qi=1,如果记ei=Mi*pi;那么:ei=0 (mod mj),j!=i;ei=1(mod mj),j=i;
很明显,e1*a1+e2*a2+···+ek*ak就是方程的一个解,加减M倍后就可以得到最小非负整数解了。
如果m1,m2,···,mk不互素,那只能两个两个求了。
x=a1 (mod m1);
x=a2 (mod m2);
解完后,a=x; m=m1和m2的最小公倍数。即可。
以下代码中a数组是除数,b数组是余数
int main()
{
double a[8], b[8], c[8], flag, max;
int i, k;
double answer = 0, plus = 1, j;
for(i = 0; i < 8; i ++)
{
scanf("%lf", &a[i]);
plus *=a[i];
}
for(i = 0; i < 8; i ++)
scanf("%lf", &b[i]);
max = a[0];
for(i = 0; i < 8;i ++)
{
max = max < a[i]? a[i]:max;
for(j = 1; ; j ++)
{
flag=j * plus/a[i];
if(fmod(flag , a[i]) == b[i]) break;//fmod(x,y)计算x/y的余数,返回x-n*y,用来计算浮点类型的
}
c[i] = flag;
answer += c[i];
}
j=fmod(answer, plus);
if(j < max) j +=plus;
printf("%.0lf\n", j);
return 0;
}
素数筛法:Eratosthenes 筛选
for(int i=2;i<=n;i++)
is_prime[i]=1;
for(int i=2;i<=n;i++)
{
if(is_prime[i])
for(intj=i*I;i<=n;i++)
is_prime[j]=0;
}
因式分解:将数分解为唯一因子分解定理中的形式。
long long factor[100][2];
int facCnt;
int getFactors(long long x)//把x进行素数分解
{
facCnt=0;
long long tmp=x;
for(int i=1;prime[i]<=tmp/prime[i];i++)
{
factor[facCnt][1]=0;
if(tmp%prime[i]==0)
{
factor[facCnt][0]=prime[i];
while(tmp%prime[i]==0)
{
factor[facCnt][1]++;
tmp/=prime[i];
}
facCnt++;
}
}
if(tmp!=1)
{
factor[facCnt][0]=tmp;
factor[facCnt++][1]=1;
}
return facCnt;
}
二分快速幂:
求解a的b次方,我们可以通过将b看成二进制来加速a的b次方的求解,比如a的17次方,可以看成是a^1*a^16,而a^16=a^8*2=a^4*2*2=a^2*2*2*2=a*2*2*2*2,只需要5次运算。通常计算a^b伴随着取模,即a^b%c,代码如下
int pow_mod(int a,int b,int mod)
{
intres=1,temp=a;
for(;b;b/=2)
{
if(b&1)res=res*temp%mod;//二进制上这一位为1,乘上这一位权值
temp=temp*temp%mod;
}
return res;
}
排列组合:
排列:从n个不同元素中取出m个的排列数。A(n,m)=n!/(n-m)!
组合:从n个不同元素中取出m个的组合数。C(n,m)=A(n,m)/m!=(n!)/(m!(n-m)!)
组合数的性质
① C(n,m)=C(n,n-m),从n个里面选m个的方案数和从n个里面选n-m个的方案数相同。
② C(n,m)=C(n-1,m)+C(n-1,m-1),从n个里面选m个的方案等于从n-1个里面选出m个的方案(不选第n个)加上从n-1个里面选出m-1个的方案(选第n个)。
可以发现性质2刚好是杨辉三角(递推式为F[i][j]=F[i-1][j]+F[i-1][j-1]),因此我们可以通过这种方式求出组合数。
矩阵快速幂:快速求矩阵的n次方
struct matrix{
intn,m;
lla[5][5];
};
matrix matrix_mul(matrix A,matrix B,intmod)
{
matrixC;
C.n=A.n;
C.m=B.m;
for(inti=0;i
欧拉函数
对正整数n,欧拉函数是少于或等于n的数中与n互质的数的数目。例如euler(8)=4,因为1,3,5,7均和8互质。
Euler函数表达通式:euler(x)=x(1-1/p1)(1-1/p2)(1-1/p3)(1-1/p4)…(1-1/pn),其中p1,p2……pn为x的所有素因数,x是不为0的整数。euler(1)=1(唯一和1互质的数就是1本身)。
欧拉公式的延伸:一个数的所有质因子之和是euler(n)*n/2。
//直接求解欧拉函数
int euler(int n){ //返回euler(n)
int res=n,a=n;
for(int i=2;i*i<=a;i++){
if(a%i==0){
res=res/i*(i-1);//先进行除法是为了防止中间数据的溢出
while(a%i==0) a/=i;
}
}
if(a>1) res=res/a*(a-1);
return res;
}
筛选法求欧拉函数,时间复杂度O(nloglogn)
void init()
{
int i, j;
memset(phi, 0, sizeof(phi));
phi[1] = 1;
for(int i = 2; i < SIZE; i++) if(!phi[i])
{
for(j = i; j < SIZE; j+=i)
{
if(!phi[j]) phi[j] = j;
phi[j] = phi[j] / i * (i-1);
}
}
return ;
}
欧拉定理:
欧拉定理表明,若n,a为正整数,且n,a互质,(a,n) = 1,则a^φ(n) ≡ 1 (mod n)
费马小定理:
假如p是质数,且a,p互质,那么 a的(p-1)次方除以p的余数恒等于1 。gdc(a,p)=1,那么a^(p-1) ≡1(mod p)