快速幂
int ksm(int x,int n,int M)
{
//if(!x && !n) return 0;
int ret=1;
while(n)
{
if(n&1) ret=1ll*ret*x%M;
x=1ll*x*x%M;
n>>=1;
}
return ret;
}
这里要特别注意 0 的 0 次方是没有意义的,按照任何数的零次方等于 1 的定义1,0 的 0 次方等于 1,但是不管多少个 0 相乘却又等于 0 。所以这里要具体情境具体调整。
龟速乘
用来处理大整数(long long 类型)的乘法取模问题,因为两个 long long 相乘可能精度会不够,所以就用 logn 的方法来做乘法。
LL multi(LL x,LL y,LL M)
{
LL ret=0;
while(y)
{
if(y&1) ret=(ret+x)%M;
x=(x<<1)%M;
y>>=1;
}
return ret;
}
费马小定理
若 为素数, ,则 。
欧拉函数
为欧拉函数,代表小于等于 且与 互质的数的个数。
-
欧拉函数是积性函数,即对于 ,有 。
-
。
-
利用唯一分解定理,设 ,则 。
直接求:
int phi(int n)
{
int ans=n;
for(int i=2;i*i<=n;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;
}
打表:
const int maxn=1e7+5;
int phi[maxn];
void euler_table(int n)
{
for(int i=0;i<=n;i++) phi[i]=i;
for(int i=2;i<=n;i++)
if(phi[i]==i)
for(int j=i;j<=n;j+=i)
phi[j]=phi[j]/i*(i-1);
}
欧拉定理
若 ,则 。
扩展欧拉定理
素性测试
Fermat素性测试,单个数,int范围内,O(logn):
bool is_prime(int x)
{
if(x==1) return 0;
if(x==2 || x==3 || x==5 || x==7) return 1;
if(ksm(2,x-1,x)!=1) return 0;
if(ksm(3,x-1,x)!=1) return 0;
if(ksm(5,x-1,x)!=1) return 0;
if(ksm(7,x-1,x)!=1) return 0;
return 1;
}
素数表,Eratosthenes筛法(埃拉托斯特尼筛法),O(nloglogn):
const int maxn=1e7+5;
bool is_prime[maxn];
void prime_table(int n)
{
for(int i=2;i<=n;i++) is_prime[i]=1;
for(int i=2;i*i<=n;i++)
if(is_prime[i])
for(int j=i*i;j<=n;j+=i)
is_prime[j]=0;
}
裴蜀定理&扩展欧几里得
扩展欧几里得算法用于求解 ,核心公式是 ,
返回值为 。
LL ex_gcd(LL a,LL b,LL &x,LL &y)
{
if(!b) {x=1; y=0; return a;}
LL r=ex_gcd(b,a%b,x,y),t=x;
x=y,y=t-a/b*y;
return r;
}
那么怎么保证一定有解呢?这个要涉及到裴蜀定理(贝祖定理):
- 设 是不全为零的整数,则存在整数 ,使得 。
一个推论: 互质当且仅当 有解。
扩展欧几里得算法可以应用到很多地方,比如求解线性同余方程。
逆元
线性同余方程 的解 称为 意义下的逆元。
假设 为质数并且 a 和 p 互质,那么可以用费马小定理来求解:
-
费马小定理: ,然后调用快速幂 即可;
int inv(int a,int p) { return ksm(a,p-2,p); }
假设 不是质数但 互质,那么可以用欧拉定理求解`;
int inv(int a,int p)
{
return ksm(a,phi(p)-1,p);
}
只要 互质,就可以用扩展欧几里得来求解:
-
扩展欧几里得: 的解 即为 的逆元(a、p互质,模p为1)。
LL inv(LL a,LL p) { LL x,y; ex_gcd(a,p,x,y); return (x+p)%p; }
假设 那么逆元不存在(!)。
线性求n个数的逆元
核心公式为 ,代码如下:
const int maxn=1e6+5;
int inv[maxn];
void get_inv(int n,int M)
{
inv[1]=1;
REP(i,2,n) inv[i]=1ll*inv[M%i]*(M-M/i)%M;
}
线性求任意n个数的逆元
设这n个数为 ,首先计算其前缀积 ,那么就可以倒着递推出每个 的逆元 ,最后 的逆元就等于 。
中国剩余定理(CRT)
就是计算这样一个问题:有一个数,它模 余 ,模 余 ,……,模 余 ,要求这个数的最小值。
中国剩余定理可以很好地解决这个问题。
模数两两互质:
LL CRT(LL n,LL *m,LL *a)
{
LL M=1,ans=0,x,y;
for(int i=1;i<=n;i++) M*=m[i];
for(int i=1;i<=n;i++)
{
LL mi=M/m[i];
ex_gcd(mi,m[i],x,y);
x=(x%m[i]+m[i])%m[i]; //最小非负解
ans=(ans+mi*x*a[i])%M;
}
return (ans+M)%M;
}
任意模数:
LL ex_CRT(int n,LL *m,LL *a) //扩展中国剩余定理
{
LL x,y,k,M=m[1],ans=a[1];
for(int i=2;i<=n;i++)
{
LL c=(a[i]-ans%m[i]+m[i])%m[i];
LL t=ex_gcd(M,m[i],x,y),bg=m[i]/t;
if(c%t) return -1;
x=c/t*x%bg; //x可能是负的
ans+=x*M;
M*=bg;
ans=(ans%M+M)%M;
}
return (ans%M+M)%M;
}
其中,m为模数数组,a为余数数组。
有时候某些数论题的模数不是质数,但是其质因子不重复,就可以分别对质因子运算然后CRT合并答案。