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;
}
若 p p p为素数, g c d ( a , p ) = 1 gcd(a,p)=1 gcd(a,p)=1,则 a p − 1 ≡ 1 ( m o d p ) a^{p-1} \equiv 1 \ (mod \ p) ap−1≡1 (mod p)。
φ ( x ) \varphi(x) φ(x)为欧拉函数,代表小于等于 x x x且与 x x x互质的数的个数。
欧拉函数是积性函数,即对于 g c d ( a , b ) = 1 gcd(a,b)=1 gcd(a,b)=1,有 φ ( a b ) = φ ( a ) φ ( b ) \varphi(ab)=\varphi(a)\varphi(b) φ(ab)=φ(a)φ(b) 。
n = ∑ d ∣ n φ ( d ) n=\sum_{d|n}\varphi(d) n=∑d∣nφ(d) 。
利用唯一分解定理,设 x = p 1 k 1 p 2 k 2 . . . p s k s x=p_1^{k_1}p_2^{k_2}...p_s^{k_s} x=p1k1p2k2...psks,则 φ ( x ) = x × ∏ i = 1 s ( 1 − 1 p i ) \varphi(x)=x\times\prod_{i=1}^{s}{(1-\frac{1}{p_i})} φ(x)=x×∏i=1s(1−pi1) 。
直接求:
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);
}
若 g c d ( a , m ) = 1 gcd(a,m)=1 gcd(a,m)=1,则 a φ ( m ) ≡ 1 ( m o d m ) a^{\varphi(m)}\equiv 1 \ (mod \ m) aφ(m)≡1 (mod m) 。
a b ≡ { a b m o d φ ( p ) , g c d ( a , p ) = 1 a b , g c d ( a , p ) ≠ 1 , b < φ ( p ) ( m o d p ) a b m o d φ ( p ) + φ ( p ) , g c d ( a , p ) ≠ 1 , b ≥ φ ( p ) a^b \equiv \left\{ \begin{array}{lr} a^{b \ mod \ \varphi(p)} ,& gcd(a,p)=1 & \\ a^b ,& gcd(a,p)\neq 1, \ b < \varphi(p) & \qquad (mod \ p) \\ a^{b \ mod \ \varphi(p) \ +\varphi(p)}, & gcd(a,p) \neq 1, \ b \geq \varphi(p) \end{array} \right. ab≡⎩⎨⎧ab mod φ(p),ab,ab mod φ(p) +φ(p),gcd(a,p)=1gcd(a,p)=1, b<φ(p)gcd(a,p)=1, b≥φ(p)(mod p)
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;
}
扩展欧几里得算法用于求解 a x + b y = g c d ( a , b ) ax+by=gcd(a,b) ax+by=gcd(a,b),核心公式是 a x 1 + b y 1 = a y 2 + b ( x 2 − ⌊ a b ⌋ y 2 ) ax_1+by_1=ay_2+b(x_2- \lfloor \frac{a}{b} \rfloor y_2) ax1+by1=ay2+b(x2−⌊ba⌋y2) ,
返回值为 g c d ( a , b ) gcd(a,b) gcd(a,b) 。
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 , b a,b a,b互质当且仅当 a x + b y = 1 ax+by=1 ax+by=1 有解。
扩展欧几里得算法可以应用到很多地方,比如求解线性同余方程。
线性同余方程 a x ≡ 1 ( m o d p ) ax \equiv 1 (mod \ p) ax≡1(mod p) 的解 x x x 称为 a m o d p a \ mod \ p a mod p 意义下的逆元。
假设 p p p 为质数并且 a 和 p 互质,那么可以用费马小定理来求解:
费马小定理: x ≡ a p − 2 ( m o d p ) x \equiv a^{p-2} (mod \ p) x≡ap−2(mod p) ,然后调用快速幂 k s m ( a , p − 2 , p ) ksm(a,p-2,p) ksm(a,p−2,p) 即可;
int inv(int a,int p)
{
return ksm(a,p-2,p);
}
假设 p p p 不是质数但 a , p a,p a,p 互质,那么可以用欧拉定理求解`;
int inv(int a,int p)
{
return ksm(a,phi(p)-1,p);
}
只要 a , p a,p a,p互质,就可以用扩展欧几里得来求解:
扩展欧几里得: a x + p y = 1 ax+py=1 ax+py=1 的解 x x x 即为 a a a 的逆元(a、p互质,模p为1)。
LL inv(LL a,LL p)
{
LL x,y;
ex_gcd(a,p,x,y);
return (x+p)%p;
}
假设 g c d ( a , p ) > 1 gcd(a,p)>1 gcd(a,p)>1 那么逆元不存在(!)。
核心公式为 x − 1 ≡ − ⌊ p x ⌋ ( p % x ) − 1 ( m o d p ) x^{-1} \equiv - \lfloor\frac{p}{x}\rfloor(p\%x)^{-1}(mod \ p) x−1≡−⌊xp⌋(p%x)−1(mod p) ,代码如下:
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个数为 a i a_i ai ,首先计算其前缀积 s i s_i si ,那么就可以倒着递推出每个 s i s_i si 的逆元 s v i = s v i + 1 a i + 1 sv_i=sv_{i+1}a_{i+1} svi=svi+1ai+1 ,最后 a i a_i ai 的逆元就等于 s v i s i − 1 sv_is_{i-1} svisi−1 。
就是计算这样一个问题:有一个数,它模 a 1 a_1 a1 余 m 1 m_1 m1 ,模 a 2 a_2 a2 余 m 2 m_2 m2 ,……,模 a n a_n an 余 m n m_n mn ,要求这个数的最小值。
中国剩余定理可以很好地解决这个问题。
模数两两互质:
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合并答案。