数论小结

求 \(N\) 的正约数集合——试除法

若 \(d\geq N\) 是 \(N\) 的约数,则 \(N/d\leq \sqrt(N)\) 也是 \(N\) 的约数。换言之,约数总是成对出现(除了对于完全平方数, \(\sqrt(N)\) 会单独出现)。

因此只需要扫描 \(d=1\sim \sqrt{N}\), 尝试 \(d\) 能否整除 \(N\) ,若能整除,则 \(N/d\) 也是 \(N\) 的约数,时间复杂度为 \(O(\sqrt{N})\)

推论:一个整数 \(N\) 的约数个数上界为 \(2\sqrt{N}\)

int inv[N],m=0;
for(int i=1;i*i<=n;i++)
{
    if(n%i==0)
    {
        inv[++m]=i;
        if(i!=n/i) inv[++m]=n/i;
    }
}

求 \(1\sim N\) 每个数的正约数集合——倍数法

对于每个数 \(d\) ,\(1\sim N\) 中以 \(d\) 为约数的数就是 \(d\) 的倍数 \(d,2d,3d,\ldots,\lfloor N/d\rfloor*d\)。

时间复杂度为 \(O(N+N/2+N/3+\ldots+N/N)=O(N\log N)\)

推论:\(1\sim N\) 每个数的约数个数的总和大约为 \(N\log N\)

vector<int> inv[N];
for(int i=1;i<=n;i++) 
    for(int j=1;j<=n/i;j++) 
        inv[i*j].push_back(i);

素数判断

单一素数判断

inline bool is_prime(int n)
{
    if(n<=1) return 0;
    for(int i=2;i*i<=n;i++) if(n%i==0) return 0;
    return 1;
}

埃氏筛

int prime[N+5];
bool vis[N+5];
inline int E_sieve(int n)
{
    int k=0;
    for(int i=0;i<=n;i++) vis[i]=0;
    for(int i=2;i<=n;i++) if(!vis[i])
    {
        prime[k++]=i;
        for(int j=2*i;j<=n;j+=i) vis[j]=1;
    }
    return k;
}

线性筛

原理:一个合数肯定有一个最小质因数,让每个合数只为它的最小质因数筛选一次,以达到不重复筛的目的

具体操作:

  1. 逐一检查 \(2\sim n\) 的所有数,第一个检查的是 \(2\),它是第一个素数
  2. 当检查到第 \(i\) 个数时,利用已经求得的素数去筛掉对应的合数 \(x\) ,而且是用 \(x\) 的最小质因数去筛
int n,v[N];
vector<int> pri;
void shai()
{
    for(int i=2;i<=n;i++)
    {
        if(v[i]==0)
        {
            v[i]=i;
            pri.push_back(i);
        }
        for(auto x:pri)
        {
            if(x>v[i]||x>n/i) break;
            v[i*x]=x;
        }
    }
}
int pri[N];
bool vis[N];
inline int euler_sieve(int n)
{
    int cnt=0;
    memset(vis,0,sizeof(vis));
    memset(pri,0,sizeof(pri));
    for(int i=2;i<=n;i++)
    {
        if(!vis[i]) pre[cnt++]=i;
        for(int j=0;j<cnt;j++)
        {
            if(i*pri[j]>n) break;
            vis[i*pri[j]]=1;
            if(i%pri[j]==0) break;
        }
    }
    return cnt;
}

gcd与lcm

gcd

gcd性质

  1. \(\gcd(a,b)=\gcd(a,a+b)=\gcd(a,k*a+b)\)
  2. \(\gcd(ka,kb)=k*\gcd(a,b)\)
  3. \(\gcd(a,b,c)=\gcd[\gcd(a,b),c]\)
  4. \(\gcd(a+cb,b)=\gcd(a,b)\)
  5. 若 \(\gcd(a,b)=d\),则 \(\gcd(a/d,b/d)=1\),即 \(a/d\) 与 \(b/d\) 互素。

欧几里得算法

用辗转相除法求gcd,即 \(\gcd(a,b)=\gcd(b,a\mod b)\)

inline int gcd(int a,int b){return b?gcd(b,a%b):a;}
inline int gcd(int a,int b)
{
    int r;
    while(b!=a){r=b;b=a%b;a=r;}
    return a;
}

更相减损法

\(\gcd(a,b)=\gcd(b,a-b)=\gcd(a,a-b)\)

inline int gcd(int a,int b)
{
    while(a!=b)
    {
        if(a>b) a-=b;
        else b-=a;
    }
    return a;
}

Stein算法

Stein算法是更相减损法的改进,求 \(\gcd(a,b)\) 时,可以分为几种情况进行优化:

  1. \(a\) 和 \(b\) 都是偶数,\(\gcd(a,b)=2\gcd(a/2,b/2)\)
  2. \(a\) 是奇数,\(b\) 是偶数。根据原理,若 \(k\) 与 \(y\) 互为质数,有\(\gcd(kx,y)=\gcd(x,y)\),所以\(\gcd(a,b)=\gcd(a,b/2)\)
  3. \(a\) 是偶数,\(b\) 是奇数。\(\gcd(a,b)=\gcd(a/2,b)\)
  4. \(a\) 和 \(b\) 都是奇数,\(\gcd(a,b)=\gcd((a+b)/2,(a-b)/2)\),或 \(\gcd(a,b)=\gcd(|a-b|,\min(a,b))\)
inline int gcd(int a,int b)
{
    if(a==0) return b;
    if(b==0) return a;
    if(a&1==0&&b&1==0) return 2*gcd(a>>1,b>>1);
    else if(a&1==0) return gcd(a>>1,b);
    else if(b&1==0) return gcd(a,b>>1);
    else return gcd((a+b)/2,(a-b)/2);
    // else return gcd(abs(a-b),min(a,b));
}

lcm

\[\begin{split} & \because\gcd(a,b)\operatorname{lcm}(a,b)=ab\\ & \therefore\operatorname{lcm}(a,b)=ab/\gcd(a,b)=a/\gcd(a,b)b \end{split} \]

inline int lcm(int a,int b){return a/gcd(a,b)*b;}

exgcd

求解 \(ax+by=c\)

\(ax+by=\gcd(a,b)\) 一定有解。

\(ax+by=c\)一定有解的前提是 \(c\%\gcd(a,b)=0\)

解\(ax+by=\gcd(a,b)\)

  1. \(b=0,\gcd(a,b)=a\),此时 \(x=1,y=0\)
  2. \(b\neq 0,\gcd(a,b)=\gcd(b,a\%b)\),

我们可以先求 \(bx+(a\%b)y=\gcd(b,a\%b)\)的解 \(\begin{cases}x=x_0\\ y=y_0\end{cases}\),然后回带

\[\begin{split} ax+by &= \gcd(a,b)\\ &= \gcd(b,a\%b)\\ &= bx_0+(a\%b)y_0 \end{split} \\ \begin{split} & \because a\%b=a-\lfloor\frac{a}{b}\rfloor b\\ & \begin{split} \therefore ax+by &= bx_0+(a-\lfloor\frac{a}{b}\rfloor b)y_0\\ &= bx_0+ay_0-\lfloor\frac{a}{b}\rfloor by_0\\ &= ay_0+b(x_0-\lfloor\frac{a}{b}\rfloor y_0) \end{split} \end{split} \]

\[\therefore ax+by=\gcd(a,b)的一个解为x=y_0,y=x_0-\lfloor\frac{a}{b}\rfloor y_0 \]

特解为 \(x_0,y_0\),通解为 \(x=x_0+\frac{b}{g}k,y=y_0-\frac{a}{g}k(g=\gcd(a,b),k\in \Z)\)

解 \(ax+by=c\)

设 \(g=\gcd(a,b)\)

先求 \(ax_0+by_0=g\)

如果 \(c\%g\neq 0\),则方程无整数解

如果 \(c\%g= 0\),则一个特解为 \(x=x_0\cdot\frac{c}{g},y=y_0\cdot\frac{c}{g}\)

通解:

\[\begin{split} x=x_0\cdot\frac{c}{g}+\frac{b}{g}k\\ y=y_0\cdot\frac{c}{g}-\frac{a}{g}k\\ \end{split} \]

解 \(ax\equiv b(\bmod m)\)

设 \(g=\gcd(a,b)\)

\(ax\equiv b(\bmod m)\Rightarrow ax+my=b\)

求逆元

\(ax\equiv 1(\bmod m)\Rightarrow ax+by=1\)

代码

递归

inline int exgcd(int a,int b,int &x,int &y)
{
    if(b==0){x=1,y=0;return a;}
    int g=exgcd(b,a%b,y,x);
    y-=a/b*x;
    return g;
}

迭代

inline int exgcd(int a,int b,int &x,int &y)
{
    int x0=1,y0=0,x1=0,y1=1,q,tmp;
    while(b)
    {
        q=a/b;
        tmp=a-q*b;
        a=b;b=tmp;
        tmp=x0-q*x1;
        x0=x1;x1=tmp;
        tmp=y0-q*y1;
        y0=y1;y1=tmp;
    }
    x=x0;y=y0;
    return a;
}

CRT

求解同余方程组

\[\begin{cases} x\equiv a_1(\bmod m_1)\\ x\equiv a_2(\bmod m_2)\\ \vdots\\ x\equiv a_k(\bmod m_k) \end{cases} \]

若 \(m_1,m_2,\cdots,m_k\) 两两互质,即对于任意 \(i\neq j\),都有 \(\gcd(m_i,m_j)=1\),则方程组在模 \(M=\prod_{i=1}^km_i\) 下有唯一解

令 \(M=\prod_{i=1}^km_i\),对于 \(i\),令 \(M_i=\frac{M}{m_i}\),因为 \(m_i\) 与其他模数互质,所以 \(\gcd(M_i,m_i)=1\),因此 \(M_i\) 在模 \(m_i\) 下存在逆元,记作 \(t_i=M_i^{-1}(\bmod m_i)\),

即 \(M_it_i\equiv 1(\bmod m_i)\Rightarrow M_it_i+m_ix=1\)

方程组的一个解为 \(x\equiv \sum_{i=1}^ka_iM_it_i(\bmod M)\),\(x_0=\sum_{i=1}^ka_i,M_it_i\)

所有解为 \(x=x_0+kM,k\in \Z\),\(x_0\) 是上述式子模 \(M\) 的最小正整数

为什么成立: 对于任意 \(j\) ,考察上述式子模 \(m_j\)

  • 当 \(i\neq j\) 时,\(M_i=\frac{M}{m_i}\) 包含了因子 \(m_j\),所以 \(M_i\equiv 0(\bmod m_i)\),对应项为 \(0\)

  • 当 \(i=j\) 时,\(M_jt_j\equiv (\bmod m_i)\),所以该项为 \(a_j*1=a_j\)

\(\because(a+b)\%m=(a\%m+b\%m)\%m\)

\(\therefore\) 设 \(b_i=a_iM_it_i,x_0=b_1+b_2+\cdots+b_n\)

考虑第 \(j\) 个式子 \(x\equiv a_j(\bmod m_j)\)

\[\begin{split}x_0\%m_j&=(b_1+b_2+\cdots+b_n)\%m_j\\&=(b_1\%m_j+b_2\%m_j+\cdots+b_n\%m_j)\%m_j\end{split} \]

\[\begin{cases}b_i\%m_j=0\quad i\neq j\\b_i\%m_j=a_i\quad i=j\end{cases} \]

\(\therefore\) 满足 \(x_0\%m_i=a_i,\forall i\in[1,n]\)

using i128=__int128_t
inline int crt(const vector<int> &a,const vector<int> &m,int n)
{
    int M=1,ans=0;
    for(int i=1;i<=n;i++) M*=m[i];
    for(int i=1;i<=n;i++)
    {
        int Mi=M/m[i],inv,tmp;
        exgcd(Mi,m[i],inv,tmp);
        inv=(inv%m[i]+m[i])%m[i];
        ans=(ans+(i128)a[i]*Mi*inv)%M;
    }
    return ans;
}

ExCRT:模数不一定互质

1. 核心思想:两两合并

假设已经求出前 \(i-1\) 个方程的解,可以表示为:

\[x \equiv A \pmod{M} \]

其中 \(M = \operatorname{lcm}(m_1, m_2, \dots, m_{i-1})\)。

现在加入第 \(i\) 个方程:

\[x \equiv a_i \pmod{m_i} \]

因为 \(x = A + M t\),代入得:

\[A + M t \equiv a_i \pmod{m_i} \]

即:

\[M t \equiv a_i - A \pmod{m_i} \]

这是一个关于 \(t\) 的线性同余方程。


2. 判断有解条件

令:

\[g = \gcd(M, m_i) \]

线性同余方程 \(M t \equiv a_i - A \pmod{m_i}\) 有解,当且仅当:

\[g \mid (a_i - A) \]

即:

\[(a_i - A) \bmod g = 0 \]

如果无解,则整个同余方程组无解。


3. 求解 \(t\)

若有解,将方程两边同时除以 \(g\):

\[\frac{M}{g} t \equiv \frac{a_i - A}{g} \pmod{\frac{m_i}{g}} \]

此时:

\[\gcd\left(\frac{M}{g}, \frac{m_i}{g}\right) = 1 \]

所以 \(\frac{M}{g}\) 在模 \(\frac{m_i}{g}\) 下存在逆元。用扩展欧几里得求出逆元 \(\text{inv}\),则:

\[t \equiv \text{inv} \cdot \frac{a_i - A}{g} \pmod{\frac{m_i}{g}} \]

取最小非负解 \(t_0\),则新的解为:

\[A' = A + M \cdot t_0 \]

新的模数为:

\[M' = \operatorname{lcm}(M, m_i) = \frac{M \cdot m_i}{g} \]

于是前 \(i\) 个方程等价于:

\[x \equiv A' \pmod{M'} \]

重复上述过程,直到合并完所有方程。

证明:判断有解条件

我们证明:线性同余方程

\[M t \equiv b \pmod{m} \]

有整数解,当且仅当

\[\gcd(M,m) \mid b \]

其中 \(b=a_i-A\),\(m=m_i\)。


1. 必要性

假设存在整数 \(t\) 满足

\[M t \equiv b \pmod{m} \]

则存在整数 \(k\),使得

\[M t - b = k m \]

即

\[b = M t - k m \]

设

\[g = \gcd(M,m) \]

因为 \(g \mid M\) 且 \(g \mid m\),所以

\[g \mid M t,\quad g \mid k m \]

因此

\[g \mid (M t - k m) = b \]

所以若方程有解,必有

\[g \mid b \]

必要性得证。


2. 充分性

假设

\[g = \gcd(M,m) \mid b \]

由裴蜀定理,存在整数 \(u,v\),使得

\[M u + m v = g \]

两边同时乘以 \(\dfrac{b}{g}\),得到

\[M \left(u\cdot \frac{b}{g}\right) + m \left(v\cdot \frac{b}{g}\right) = b \]

令

\[t = u\cdot \frac{b}{g} \]

则有

\[M t + m \left(v\cdot \frac{b}{g}\right) = b \]

即

\[M t \equiv b \pmod{m} \]

所以 \(t\) 就是方程的一个整数解。

因此,只要 \(g \mid b\),方程就一定有解。


3. 结论

综合必要性和充分性,线性同余方程

\[M t \equiv b \pmod{m} \]

有整数解,当且仅当

\[\gcd(M,m) \mid b \]

在 ExCRT 中,\(M\) 是前 \(i-1\) 个方程解对应的模数,\(m=m_i\),\(b=a_i-A\)。因此判断第 \(i\) 个方程能否与前面的解合并,条件就是:

\[\gcd(M,m_i) \mid (a_i-A) \]

如果这个条件不满足,则整个同余方程组无解。

\(\{A,M\}\) 表示 \(x\equiv A(\bmod M)\),无解返回 \(\{-1,-1\}\)

using i128=__int128_t
inline pair<int,int> excrt(const vector<int> &a,const vector<int> &m,int n)
{
    int A=a[1],M=m[1];
    for(int i=2;i<=n;i++)
    {
        int x,y;
        int g=exgcd(M,m[i],x,y);
        if((a[i]-A)%g) return {-1,-1};
        int m1=m[i]/g;
        int t=((a[i]-A)/g%m1+m1)%m1;
        int inv,tmp;
        exgcd(M/g,m1,inv,tmp);
        inv=(inv%m1+m1)%m1;
        int k=t*inv%m1;
        int newM=(i128)M/g*m[i];
        A=(int)(((i128)A+(i128)M*k)%newM);
        M=newM;
        A=(A%M+M)%M;
    }
    return {A,M};
}
posted @ 2026-10-29 16:32  Aries2011  阅读(7)  评论(0)    收藏  举报