数学I-数论

前言

注:本文一切代码建立在此基础上

#include<bits/stdc++.h>
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(),(x).end()
using namespace std;
using ll=long long;
using PI=pair<int,int>;
struct IO
{
    char rbuf[1<<20],wbuf[1<<20],*p,*pp,*wp;
    IO():p(rbuf),pp(rbuf),wp(wbuf){}
    #define gc() (p==pp&&(pp=rbuf+fread(rbuf,1,1<<20,stdin),p=rbuf),p==pp?EOF:*p++)
    #define pc(c) do{if(wp-wbuf==(1<<20))flush();*wp++=(c);}while(0)
    IO&operator<<(char c){pc(c);return*this;}
    IO&operator<<(const char*s){while(*s)pc(*s++);return*this;}
    IO&operator>>(char*s){char c=gc();while(c<=32)c=gc();while(c>32)*s++=c,c=gc();*s='\0';return*this;}
    template<typename T>IO&operator>>(T&x){x=0;char c=gc();bool f=0;while(c<'0'||c>'9')f|=(c=='-'),c=gc();while(c>='0'&&c<='9')x=x*10+(c^48),c=gc();if(f)x=-x;return*this;}
    template<typename T>IO&operator<<(T x){if(x<0)pc('-'),x=-x;char t[30];int c=0;do{t[c++]=x%10+'0';}while(x/=10);while(c--)pc(t[c]);return*this;}
    void flush(){if(wp>wbuf)fwrite(wbuf,1,wp-wbuf,stdout),wp=wbuf;}
    ~IO(){flush();}
}io;
#define cin io
#define cout io

一、整除与质数

1.1 整除基本性质

定义\(a\mid b \iff \exists k\in\mathbb{Z},\ b=ak\)

性质 表述 证明思路
传递性 \(a\mid b\land b\mid c\Rightarrow a\mid c\) \(b=ak_1,c=bk_2\Rightarrow c=a(k_1k_2)\)
线性组合 \(a\mid b\land a\mid c\Rightarrow a\mid (xb+yc)\) \(b=ak_1,c=ak_2\Rightarrow xb+yc=a(xk_1+yk_2)\)
乘法保序 \(a\mid b\Rightarrow ac\mid bc\ (c\neq0)\) \(b=ak\Rightarrow bc=(ac)k\)
大小约束 \(a\mid b\land b\neq0\Rightarrow |a|\le|b|\) \(b=ak\Rightarrow |b|=|a||k|\ge|a|\)

1.2 质数判定:试除法优化

定理:若 \(n\) 为合数,则 \(\exists d\mid n,\ 2\le d\le\sqrt{n}\)

证明:设 \(n=ab,\ a\le b\),则 \(a^2\le ab=n\Rightarrow a\le\sqrt{n}\),取 \(d=a\) 即可。

代码实现\(O(\sqrt{n})\),跳过偶数优化):

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

易错点

  • i*i<=n 可能溢出,建议写 i<=n/i 或用 long long
  • 特判 \(n=2\) 和偶数,减少一半循环

1.3 线性筛(欧拉筛)正确性证明

核心思想:每个合数 \(x\) 仅被其最小质因子筛掉一次。

算法流程

const int N=1e7+5;
int pr[N],cnt;
bool vis[N];

void init(int n)
{
    for(int i=2;i<=n;i++)
    {
        if(!vis[i]) pr[++cnt]=i;
        for(int j=1;j<=cnt&&i*pr[j]<=n;j++)
        {
            vis[i*pr[j]]=1;
            if(i%pr[j]==0)break;  // 关键:保证最小质因子
        }
    }
}

正确性证明

  1. 每个合数都会被筛:设 \(x\) 最小质因子为 \(p\),则 \(x=p\cdot k\)。当外层循环到 \(k\) 时,内层枚举到 \(p\) 必筛 \(x\)
  2. 每个合数只被筛一次:若 \(x\)\(p_1\) 筛(\(p_1\) 最小质因子),则 \(x=p_1\cdot k_1\)\(p_1\mid k_1\)break,后续更大质因子不会再次筛 \(x\)

复杂度\(O(n)\),每个数进内层循环最多一次。

1.4 质因数分解(试除法 + 唯一分解定理)

唯一分解定理\(\forall n>1,\ n=\prod_{i=1}^k p_i^{e_i}\)\(p_i\) 互异质数,表示唯一。

代码\(O(\sqrt{n})\)):

vector<PI> fac;
for(int i=2;i*i<=n;i++)
    if(n%i==0)
    {
        int c=0;
        while(n%i==0) n/=i,c++;
        fac.pb({i,c});
    }
if(n>1)fac.pb({n,1});  // 剩余大质数

应用:求约数个数 \(\tau(n)=\prod(e_i+1)\),约数和 \(\sigma(n)=\prod\frac{p_i^{e_i+1}-1}{p_i-1}\)

例题:P2429 质数生成器

题目:求 \([L,R]\) 内所有质数,\(1\le L\le R\le 10^{12},\ R-L\le 10^6\)

思路:区间筛法(分段筛)

  1. 先筛出 \(\le\sqrt{R}\) 的所有质数
  2. 用这些质数去标记 \([L,R]\) 中的合数
const int M=1e6+5;
bool vis[M],seg[M];
int pr[M],cnt;

void init(int n)  // 筛 sqrt(R) 内质数
{
    for(int i=2;i<=n;i++)
    {
        if(!vis[i]) pr[++cnt]=i;
        for(int j=1;j<=cnt&&i*pr[j]<=n;j++)
        {
            vis[i*pr[j]]=1;
            if(i%pr[j]==0)break;
        }
    }
}

void solve(ll L,ll R)
{
    memset(seg,0,sizeof(seg));
    if(L==1) seg[0]=1;  // 1不是质数
    for(int i=1;i<=cnt&&1ll*pr[i]*pr[i]<=R;i++)
    {
        ll st=max(1ll*pr[i]*pr[i],(L+pr[i]-1)/pr[i]*pr[i]);
        for(ll j=st;j<=R;j+=pr[i]) seg[j-L]=1;
    }
    for(int i=0;i<=R-L;i++)
        if(!seg[i]) cout<<L+i<<'\n';
}

二、最大公约数与扩展欧几里得

2.1 欧几里得算法

定理\(\gcd(a,b)=\gcd(b,a\bmod b)\)

证明:设 \(d=\gcd(a,b)\),则 \(d\mid a,d\mid b\Rightarrow d\mid(a-qb)=a\bmod b\);反之同理,故两式公约数集合相同。

迭代实现(避免递归栈):

int gcd(int a,int b){while(b)a%=b,swap(a,b);return a;}

复杂度证明(拉梅定理)

  • 最坏情况为斐波那契数列相邻项:\(\gcd(F_{k+1},F_k)\)
  • 每次取模至少减半,故复杂度 \(O(\log\min(a,b))\)

2.2 扩展欧几里得

定理\(\forall a,b\in\mathbb{Z},\ \exists x,y\in\mathbb{Z},\ ax+by=\gcd(a,b)\)

递归推导

已知:b·x' + (a%b)·y' = gcd(b, a%b) = gcd(a,b)
展开:b·x' + (a - ⌊a/b⌋·b)·y' = gcd
整理:a·y' + b·(x' - ⌊a/b⌋·y') = gcd
故:x = y', y = x' - ⌊a/b⌋·y'

代码

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

通解结构
\((x_0,y_0)\)\(ax+by=c\) 的一组特解,\(d=\gcd(a,b)\),则通解为:

\[x=x_0+k\cdot\frac{b}{d},\quad y=y_0-k\cdot\frac{a}{d}\quad(k\in\mathbb{Z}) \]

应用:解线性同余方程 \(ax\equiv c\pmod b\)

  1. 等价于 \(ax+by=c\)
  2. 有解 \(\iff d=\gcd(a,b)\mid c\)
  3. 特解 \(x_0\)\(\frac{c}{d}\),最小正整数解 \((x_0\cdot\frac{c}{d}\bmod\frac{b}{d}+\frac{b}{d})\bmod\frac{b}{d}\)

例题:P1516 青蛙的约会

题目:两只青蛙从 \(x,y\) 出发,每次跳 \(m,n\) 米,跑道长 \(L\),问何时相遇。

建模:求最小 \(t\ge0\) 使 \((x+mt)\equiv(y+nt)\pmod L\)
\(\Rightarrow (m-n)t\equiv(y-x)\pmod L\)
\(\Rightarrow (m-n)t+Lk=y-x\)

代码

ll exgcd(ll a,ll b,ll&x,ll&y)
{
    if(!b){x=1,y=0;return a;}
    ll d=exgcd(b,a%b,y,x);
    y-=a/b*x;
    return d;
}

int main()
{
    ll x,y,m,n,L,a,b,c,d,xx,yy;
    cin>>x>>y>>m>>n>>L;
    a=m-n;b=L;c=y-x;
    if(a<0) a=-a,c=-c;  // 保证a非负
    d=exgcd(a,b,xx,yy);
    if(c%d){cout<<"Impossible\n";return 0;}
    ll t=xx*(c/d)%(b/d);
    cout<<(t+b/d)%(b/d)<<'\n';
    return 0;
}

三、同余与模运算

3.1 同余性质补充证明

可除性条件\(ac\equiv bc\pmod m\Rightarrow a\equiv b\pmod{\frac{m}{\gcd(c,m)}}\)

证明\(m\mid c(a-b)\),设 \(d=\gcd(c,m)\),则 \(\frac{m}{d}\mid\frac{c}{d}(a-b)\),而 \(\gcd(\frac{c}{d},\frac{m}{d})=1\),故 \(\frac{m}{d}\mid(a-b)\)

3.2 快速幂原理与二进制分解

核心\(a^b=\prod_{i:bit_i=1}a^{2^i}\)

迭代实现

ll qpow(ll a,ll b,ll p)
{
    ll res=1;
    for(;b;b>>=1,a=a*a%p)
        if(b&1) res=res*a%p;
    return res;
}

复杂度\(O(\log b)\),每轮平方 + 条件乘法

3.3 乘法逆元三种方法对比

方法 条件 复杂度 适用场景
费马小定理 \(p\) 质数,\(p\nmid a\) \(O(\log p)\) 单点查询,模数固定质数
扩展欧几里得 \(\gcd(a,m)=1\) \(O(\log m)\) 模数任意,单点查询
线性递推 \(p\) 质数,求 \(1\sim n\) 逆元 \(O(n)\) 批量预处理

线性递推公式推导
\(p=ki+r\ (0<r<i)\),则 \(ki+r\equiv0\pmod p\)
\(\Rightarrow r\equiv-ki\pmod p\)
\(\Rightarrow i^{-1}\equiv-k\cdot r^{-1}\equiv-\lfloor p/i\rfloor\cdot(p\bmod i)^{-1}\pmod p\)

代码

inv[1]=1;
for(int i=2;i<=n;i++) inv[i]=(p-p/i)*inv[p%i]%p;

例题:P5431 逆元前缀和

题目:给定 \(n,p\),求 \(\sum_{i=1}^n i^{-1}\pmod p\)\(p\) 为质数。

思路:线性递推求逆元 + 前缀和

const int N=5e6+5;
ll inv[N],sum[N];
int n,p;

int main()
{
    cin>>n>>p;
    inv[1]=sum[1]=1;
    for(int i=2;i<=n;i++)
    {
        inv[i]=(p-p/i)*inv[p%i]%p;
        sum[i]=(sum[i-1]+inv[i])%p;
    }
    cout<<sum[n]<<'\n';
    return 0;
}

四、欧拉函数

4.1 定义与公式推导

定义\(\varphi(n)=\|\{1\le k\le n:\gcd(k,n)=1\}\|\)

公式推导(容斥原理):

\[\varphi(n)=n\prod_{p\mid n}(1-\frac{1}{p})=n\prod_{p\mid n}\frac{p-1}{p} \]

证明:从 \(1\sim n\) 中剔除所有 \(p\) 的倍数,对每个质因子 \(p\) 保留比例为 \(\frac{p-1}{p}\)

积性证明:若 \(\gcd(a,b)=1\),则 \(\mathbb{Z}_{ab}^*\cong\mathbb{Z}_a^*\times\mathbb{Z}_b^*\)(中国剩余定理),故 \(\varphi(ab)=\varphi(a)\varphi(b)\)

4.2 线性筛 \(\varphi\) 的更新规则推导

关键性质

  • \(p\) 质数:\(\varphi(p)=p-1\)
  • \(p\mid i\)\(\varphi(ip)=\varphi(i)\cdot p\)\(p\) 已在 \(i\) 的质因子中)
  • \(p\nmid i\)\(\varphi(ip)=\varphi(i)\cdot(p-1)\)(积性)

代码

void init_phi(int n)
{
    phi[1]=1;
    for(int i=2;i<=n;i++)
    {
        if(!vis[i])
        {
            pr[++cnt]=i;
            phi[i]=i-1;
        }
        for(int j=1;j<=cnt&&i*pr[j]<=n;j++)
        {
            vis[i*pr[j]]=1;
            if(i%pr[j]==0)
            {
                phi[i*pr[j]]=phi[i]*pr[j];
                break;
            }
            else phi[i*pr[j]]=phi[i]*(pr[j]-1);
        }
    }
}

4.3 扩展欧拉定理使用条件详解

定理

\[a^b\equiv\begin{cases} a^b & b<\varphi(m)\\ a^{b\bmod\varphi(m)+\varphi(m)} & b\ge\varphi(m) \end{cases}\pmod m\]

注意

  • \(\gcd(a,m)\) 任意,不要求互质
  • 仅当 \(b\ge\varphi(m)\) 时才加 \(\varphi(m)\),否则直接用 \(b\)
  • 递归降幂时需判断指数大小

例题:P2568 GCD

题目:求 \(\sum_{i=1}^n\sum_{j=1}^n[\gcd(i,j)\text{ is prime}]\)

推导

\[\begin{aligned} ans&=\sum_{p\text{ prime}}\sum_{i=1}^n\sum_{j=1}^n[\gcd(i,j)=p]\\ &=\sum_{p}\sum_{i=1}^{\lfloor n/p\rfloor}\sum_{j=1}^{\lfloor n/p\rfloor}[\gcd(i,j)=1]\\ &=\sum_{p}(2\sum_{k=1}^{\lfloor n/p\rfloor}\varphi(k)-1) \end{aligned}\]

代码

const int N=1e7+5;
int phi[N],pr[N],cnt;
ll sum[N];
bool vis[N];

void init(int n)
{
    phi[1]=1;
    for(int i=2;i<=n;i++)
    {
        if(!vis[i])
        {
            pr[++cnt]=i;
            phi[i]=i-1;
        }
        for(int j=1;j<=cnt&&i*pr[j]<=n;j++)
        {
            vis[i*pr[j]]=1;
            if(i%pr[j]==0)
            {
                phi[i*pr[j]]=phi[i]*pr[j];
                break;
            }
            else phi[i*pr[j]]=phi[i]*(pr[j]-1);
        }
    }
    for(int i=1;i<=n;i++) sum[i]=sum[i-1]+phi[i];
}

int main()
{
    int n;
    cin>>n;
    init(n);
    ll ans=0;
    for(int i=1;i<=cnt;i++) ans+=2*sum[n/pr[i]]-1;
    cout<<ans<<'\n';
    return 0;
}

五、中国剩余定理

5.1 CRT 构造性证明

问题:解 \(x\equiv a_i\pmod{m_i}\)\(m_i\) 两两互质。

构造

  1. \(M=\prod m_i\)\(M_i=M/m_i\)
  2. \(t_i\equiv M_i^{-1}\pmod{m_i}\)\(M_i\)\(m_i\) 互质,逆元存在)
  3. \(x=\sum a_iM_it_i\pmod M\)

验证:对第 \(j\) 个方程,\(i\neq j\)\(M_i\equiv0\pmod{m_j}\),仅 \(i=j\) 项贡献 \(a_jM_jt_j\equiv a_j\pmod{m_j}\)

唯一性:若 \(x_1,x_2\) 均为解,则 \(x_1-x_2\equiv0\pmod{m_i}\ \forall i\),由互质性得 \(x_1\equiv x_2\pmod M\)

5.2 EXCRT 两两合并推导

合并两个方程

\[\begin{cases} x\equiv a_1\pmod{m_1}\\ x\equiv a_2\pmod{m_2} \end{cases}\]

步骤

  1. \(x=a_1+m_1y\),代入得 \(m_1y\equiv a_2-a_1\pmod{m_2}\)
  2. \(d=\gcd(m_1,m_2)\),有解 \(\iff d\mid(a_2-a_1)\)
  3. 用 exgcd 解 \(m_1y+m_2k=a_2-a_1\) 得特解 \(y_0\)
  4. 通解 \(y=y_0+t\cdot\frac{m_2}{d}\),代回得 \(x\equiv a_1+m_1y_0\pmod{\operatorname{lcm}(m_1,m_2)}\)

代码

ll excrt(ll a[],ll m[],int k)
{
    ll M=m[1],ans=a[1];
    for(int i=2;i<=k;i++)
    {
        ll x,y,c=(a[i]-ans)%m[i];
        if(c<0) c+=m[i];
        ll d=exgcd(M,m[i],x,y);
        if(c%d) return -1;  // 无解
        ll mod=m[i]/d;
        x=(x*(c/d)%mod+mod)%mod;
        ans+=M*x;
        M*=mod;
        ans%=M;
    }
    return (ans+M)%M;
}

例题:P4777 扩展中国剩余定理

注意

  • 使用 __int128 或慢速乘防溢出
  • 无解时返回 -1
ll mul(ll a,ll b,ll p)  // 慢速乘防溢出
{
    ll res=0;
    for(;b;b>>=1,a=(a+a)%p)if(b&1)res=(res+a)%p;
    return res;
}

ll exgcd(ll a,ll b,ll&x,ll&y)
{
    if(!b){x=1,y=0;return a;}
    ll d=exgcd(b,a%b,y,x);
    y-=a/b*x;
    return d;
}

ll excrt(ll a[],ll m[],int k)
{
    ll M=m[1],ans=a[1];
    for(int i=2;i<=k;i++)
    {
        ll x,y,c=(a[i]-ans)%m[i];
        if(c<0) c+=m[i];
        ll d=exgcd(M,m[i],x,y);
        if(c%d) return -1;
        ll mod=m[i]/d;
        x=mul(x,c/d,mod);
        ans+=mul(M,x,M*mod);
        M*=mod;
        ans%=M;
    }
    return (ans+M)%M;
}

六、莫比乌斯反演

6.1 狄利克雷卷积视角

定义\((f*g)(n)=\sum_{d\mid n}f(d)g(\frac{n}{d})\)

关键函数

  • \(\varepsilon(n)=[n=1]\)(单位元)
  • \(I(n)=1\)(常函数)
  • \(\operatorname{id}(n)=n\)(恒等函数)

性质\(\mu*I=\varepsilon\)(莫比乌斯反演核心)

6.2 反演公式推导

因数形式

\[F(n)=\sum_{d\mid n}f(d)\iff f(n)=\sum_{d\mid n}\mu(d)F(\frac{n}{d}) \]

证明

\[\begin{aligned} \sum_{d\mid n}\mu(d)F(\frac{n}{d})&=\sum_{d\mid n}\mu(d)\sum_{k\mid\frac{n}{d}}f(k)\\ &=\sum_{k\mid n}f(k)\sum_{d\mid\frac{n}{k}}\mu(d)\\ &=\sum_{k\mid n}f(k)\varepsilon(\frac{n}{k})=f(n) \end{aligned}\]

6.3 经典模型:\(\gcd\) 计数

问题\(\sum_{i=1}^n\sum_{j=1}^m[\gcd(i,j)=1]\)

推导

\[\begin{aligned} &=\sum_{i=1}^n\sum_{j=1}^m\sum_{d\mid\gcd(i,j)}\mu(d)\\ &=\sum_{d=1}^{\min(n,m)}\mu(d)\sum_{i=1}^{\lfloor n/d\rfloor}\sum_{j=1}^{\lfloor m/d\rfloor}1\\ &=\sum_{d=1}^{\min(n,m)}\mu(d)\lfloor\frac{n}{d}\rfloor\lfloor\frac{m}{d}\rfloor \end{aligned}\]

整除分块优化\(\lfloor\frac{n}{d}\rfloor\) 取值 \(O(\sqrt{n})\) 种,预处理 \(\mu\) 前缀和后 \(O(\sqrt{n})\) 计算。

例题:P3455 莫比乌斯反演+整除分块

题目\(T\) 组询问,求 \(\sum_{i=1}^n\sum_{j=1}^m[\gcd(i,j)=k]\)

代码

const int N=5e4+5;
int mu[N],sum[N],pr[N],cnt;
bool vis[N];

void init()
{
    mu[1]=1;
    for(int i=2;i<N;i++)
    {
        if(!vis[i])
        {
            pr[++cnt]=i;
            mu[i]=-1;
        }
        for(int j=1;j<=cnt&&i*pr[j]<N;j++)
        {
            vis[i*pr[j]]=1;
            if(i%pr[j]==0)
            {
                mu[i*pr[j]]=0;
                break;
            }
            else mu[i*pr[j]]=-mu[i];
        }
    }
    for(int i=1;i<N;i++) sum[i]=sum[i-1]+mu[i];
}

ll solve(int n,int m)
{
    if(n>m) swap(n,m);
    ll res=0;
    for(int l=1,r;l<=n;l=r+1)
    {
        r=min(n/(n/l),m/(m/l));
        res+=1ll*(sum[r]-sum[l-1])*(n/l)*(m/l);
    }
    return res;
}

int main()
{
    init();
    int T;
    cin>>T;
    while(T--)
    {
        int a,b,c,d,k;
        cin>>a>>b>>c>>d>>k;
        ll ans=solve(b/k,d/k)-solve((a-1)/k,d/k)-solve(b/k,(c-1)/k)+solve((a-1)/k,(c-1)/k);
        cout<<ans<<'\n';
    }
    return 0;
}

七、整除分块

7.1 取值种数与右端点公式证明

引理\(\lfloor\frac{n}{i}\rfloor\) 的不同取值个数 \(\le 2\sqrt{n}\)

证明

  • \(i\le\sqrt{n}\),最多 \(\sqrt{n}\)
  • \(i>\sqrt{n}\)\(\lfloor\frac{n}{i}\rfloor<\sqrt{n}\),最多 \(\sqrt{n}\)

右端点公式:对给定 \(l\),使 \(\lfloor\frac{n}{i}\rfloor\) 不变的最大 \(r=\lfloor\frac{n}{\lfloor n/l\rfloor}\rfloor\)

证明:设 \(q=\lfloor n/l\rfloor\),则 \(ql\le n<q(l+1)\)
\(r\) 满足 \(qr\le n<q(r+1)\Rightarrow r\le n/q\),故 \(r=\lfloor n/q\rfloor\)

7.2 多变量分块技巧

问题\(\sum_{i=1}^n f(\lfloor\frac{n}{i}\rfloor,\lfloor\frac{m}{i}\rfloor)\)

技巧:右端点取 \(r=\min(\lfloor\frac{n}{\lfloor n/l\rfloor}\rfloor,\lfloor\frac{m}{\lfloor m/l\rfloor}\rfloor)\)

例题:P4213 杜教筛前置

题目:求 \(\sum_{i=1}^n\varphi(i)\)\(n\le10^{10}\)(需杜教筛,此处展示分块思想)

分块框架

ll calc(int n)
{
    ll res=0;
    for(int l=1,r;l<=n;l=r+1)
    {
        r=n/(n/l);
        res+=1ll*(sum[r]-sum[l-1])*(n/l);  // sum 为预处理前缀和
    }
    return res;
}

八、杜教筛

8.1 核心公式推导

目标:求 \(S(n)=\sum_{i=1}^n f(i)\)

构造:找 \(g\) 使 \(h=f*g\) 易求前缀和,则:

\[\begin{aligned} \sum_{i=1}^n h(i)&=\sum_{i=1}^n\sum_{d\mid i}f(d)g(\frac{i}{d})\\ &=\sum_{d=1}^n g(d)\sum_{k=1}^{\lfloor n/d\rfloor}f(k)\\ &=\sum_{d=1}^n g(d)S(\lfloor n/d\rfloor) \end{aligned}\]

移项

\[g(1)S(n)=\sum_{i=1}^n h(i)-\sum_{d=2}^n g(d)S(\lfloor n/d\rfloor) \]

8.2 函数选择策略

目标 \(f\) 辅助 \(g\) \(h=f*g\) \(S(n)\) 表达式
\(\mu\) \(I\) \(\varepsilon\) \(S_\mu(n)=1-\sum_{d=2}^n S_\mu(\lfloor n/d\rfloor)\)
\(\varphi\) \(I\) \(\operatorname{id}\) \(S_\varphi(n)=\frac{n(n+1)}{2}-\sum_{d=2}^n S_\varphi(\lfloor n/d\rfloor)\)

例题:P4213 杜教筛模板

// 预处理范围:N=5e6 时,时间≈0.5s,空间≈80MB
// 查询复杂度:$O(n^{2/3})$,哈希表记忆化避免重复计算
unordered_map<int,int>s_mu;
unordered_map<int,ll>s_phi;

int get_mu(int n)
{
    if(n<N) return mu[n];  // 预处理部分直接返回
    if(s_mu.count(n)) return s_mu[n];
    int res=1;  // ∑ε(i) = 1
    for(int l=2,r;l<=n;l=r+1)
    {
        r=n/(n/l);
        res-=(r-l+1)*get_mu(n/l);  // g(d)=I(d)=1,系数为区间长度
    }
    return s_mu[n]=res;
}

九、组合数取模

9.1 阶乘逆元预处理细节

注意

  • 先求 fac[n],再用费马小定理求 ifac[n],倒推 ifac[i]=ifac[i+1]*(i+1)%p
  • 避免对每个 i 单独求逆,复杂度 \(O(n+\log p)\)

9.2 Lucas 定理证明简述

定理\(\binom{n}{m}\equiv\prod\binom{n_i}{m_i}\pmod p\)\(n_i,m_i\)\(p\) 进制位

核心\((1+x)^p\equiv1+x^p\pmod p\)(二项式系数 \(\binom{p}{k}\equiv0\ (0<k<p)\)

例题:P2480 古代猪文

题目:求 \(G^{\sum_{d\mid n}\binom{n}{d}}\bmod 999911659\)

思路

  1. 模数 \(P=999911659\) 质数,指数对 \(P-1\) 取模(费马小定理)
  2. \(P-1=2×3×4679×35617\),对每个质因子用 Lucas 求组合数和
  3. EXCRT 合并结果,最后快速幂

代码框架

ll p[4]={2,3,4679,35617},a[4];
ll calc(ll n,ll m,ll P)  // Lucas 小模数版本
{
    if(m>n) return 0;
    ll res=1;
    while(n)
    {
        ll ni=n%P,mi=m%P;
        if(mi>ni) return 0;
        for(int i=1;i<=mi;i++) res=res*(ni-i+1)%P*qpow(i,P-2,P)%P;
        n/=P;m/=P;
    }
    return res;
}

int main()
{
    ll n,G;
    cin>>n>>G;
    if(G%MOD==0){cout<<0<<'\n';return 0;}  // 特判
    for(int i=1;i*i<=n;i++)
        if(n%i==0)
        {
            for(int j=0;j<4;j++) a[j]=(a[j]+calc(n,i,p[j]))%p[j];
            if(i*i!=n)
                for(int j=0;j<4;j++) a[j]=(a[j]+calc(n,n/i,p[j]))%p[j];
        }
    ll ans=excrt(a,p,4);  // 合并指数
    cout<<qpow(G,ans,MOD)<<'\n';
    return 0;
}

十、阶与原根

10.1 阶的定义与性质

定义:设 \(a,n\) 互质,满足 \(a^k\equiv1\pmod n\) 的最小正整数 \(k\) 称为 \(a\)\(n\) 的阶,记作 \(\operatorname{ord}_n(a)\)

性质 1:若 \(a^m\equiv1\pmod n\),则 \(\operatorname{ord}_n(a)\mid m\)

证明:设 \(k=\operatorname{ord}_n(a)\),由带余除法 \(m=qk+r\ (0\le r<k)\),则 \(a^m\equiv a^{qk+r}\equiv (a^k)^q a^r\equiv a^r\equiv1\),故 \(a^r\equiv1\),由 \(k\) 的最小性得 \(r=0\),所以 \(k\mid m\)

性质 2\(\operatorname{ord}_n(a)\mid\varphi(n)\)
证明:由欧拉定理 \(a^{\varphi(n)}\equiv1\pmod n\),结合性质 1 即得。

10.2 原根的定义与存在性

定义:若 \(g\) 满足 \(\operatorname{ord}_n(g)=\varphi(n)\),则称 \(g\) 是模 \(n\) 的一个原根。

原根存在定理:模 \(n\) 存在原根当且仅当 \(n=2,4,p^k,2p^k\),其中 \(p\) 为奇质数,\(k\ge1\)
(证明较复杂,本文从略)

10.3 原根的求法

步骤

  1. 求出 \(\varphi(n)\) 的所有不同质因子 \(p_1,p_2,\dots,p_s\)
  2. \(2\) 开始枚举 \(g\),判断是否满足 \(g^{\varphi(n)/p_i}\not\equiv1\pmod n\) 对所有 \(i\) 成立。若成立,则 \(g\) 为原根。

正确性证明:若 \(g\) 不是原根,则存在 \(d<\varphi(n)\) 使 \(g^d\equiv1\),则 \(d\) 整除 \(\varphi(n)\),且存在某个质因子 \(p_i\) 使 \(d\mid\frac{\varphi(n)}{p_i}\),从而 \(g^{\varphi(n)/p_i}\equiv1\)。因此若所有 \(g^{\varphi(n)/p_i}\not\equiv1\),则 \(g\) 必为原根。

10.4 指标(离散对数)

\(g\) 是模 \(n\) 的原根,则每个与 \(n\) 互质的数 \(a\) 可唯一表示为 \(a\equiv g^{\operatorname{ind}_g(a)}\pmod n\),其中 \(0\le \operatorname{ind}_g(a)<\varphi(n)\),称为 \(a\) 关于 \(g\) 的指标。

性质

  • \(\operatorname{ind}_g(ab)\equiv\operatorname{ind}_g(a)+\operatorname{ind}_g(b)\pmod{\varphi(n)}\)
  • \(\operatorname{ind}_g(a^k)\equiv k\cdot\operatorname{ind}_g(a)\pmod{\varphi(n)}\)

例题:P6091 【模板】原根

vector<int> get_factors(int x)  // 求 x 的所有不同质因子
{
    vector<int> fac;
    for(int i=2;i*i<=x;i++)
        if(x%i==0)
        {
            fac.pb(i);
            while(x%i==0) x/=i;
        }
    if(x>1) fac.pb(x);
    return fac;
}

bool has_root(int n)  // 判断是否有原根
{
    if(n==2||n==4) return 1;
    vector<int> fac=get_factors(n);
    if(fac.size()!=1&&fac.size()!=2) return 0;  // 不是 p^k 或 2p^k 形式
    if(fac.size()==2&&fac[0]!=2) return 0;      // 2p^k 中第一个因子必须是2
    return 1;
}

int get_root(int n)
{
    if(!has_root(n)) return 0;
    int phi=Phi(n);  // 欧拉函数
    vector<int> fac=get_factors(phi);
    for(int g=2;g<n;g++)
    {
        if(gcd(g,n)!=1) continue;
        int ok=1;
        for(int p:fac)
            if(qpow(g,phi/p,n)==1){ok=0;break;}
        if(ok) return g;
    }
    return 0;
}

十一、二次剩余

11.1 定义与欧拉判别法

定义:设 \(p\) 为奇质数,\(a\)\(p\) 互质。若存在 \(x\) 使得 \(x^2\equiv a\pmod p\),则称 \(a\) 是模 \(p\)二次剩余,否则称为二次非剩余

欧拉判别法

\[a^{(p-1)/2}\equiv\begin{cases} 1\pmod p, & a\text{ 是二次剩余}\\ -1\pmod p, & a\text{ 是二次非剩余} \end{cases}\]

证明

  1. \(a\) 是二次剩余,即 \(a\equiv x^2\),则 \(a^{(p-1)/2}\equiv x^{p-1}\equiv1\pmod p\)(费马小定理)。
  2. \(a\) 不是二次剩余,考虑 \(p\) 的简化剩余系,将其配对为 \((x,ax^{-1})\),每对乘积为 \(a\),且 \(x\neq ax^{-1}\)(否则 \(a\equiv x^2\))。共 \((p-1)/2\) 对,故 \(1\cdot2\cdots(p-1)\equiv a^{(p-1)/2}\pmod p\)。而左边为 \((p-1)! \equiv -1\pmod p\)(威尔逊定理),因此 \(a^{(p-1)/2}\equiv -1\)

11.2 勒让德符号

定义勒让德符号 \(\left(\frac{a}{p}\right)=a^{(p-1)/2}\bmod p\),值为 \(1\)\(-1\)

性质

  • 完全积性:\(\left(\frac{ab}{p}\right)=\left(\frac{a}{p}\right)\left(\frac{b}{p}\right)\)
  • 高斯引理:可用于计算 \(\left(\frac{2}{p}\right)=(-1)^{(p^2-1)/8}\)

11.3 Cipolla 算法求解二次剩余

问题:给定 \(p\)(奇质数)和 \(n\),求 \(x\) 使 \(x^2\equiv n\pmod p\)

算法步骤

  1. \(n\equiv0\),返回 \(0\)
  2. \(n^{(p-1)/2}\equiv -1\),无解。
  3. 随机选取 \(a\) 使得 \(a^2-n\) 不是二次剩余,即 \((a^2-n)^{(p-1)/2}\equiv -1\)
  4. 定义 \(\omega=\sqrt{a^2-n}\),在扩域 \(\mathbb{F}_p[\omega]\) 中计算 \((a+\omega)^{(p+1)/2}\),得到的结果即为一个解。

正确性证明

  • 在扩域中,\(\omega^2=a^2-n\)
  • \((a+\omega)^p\equiv a^p+\omega^p\equiv a-\omega\)(因为 \(p\) 是奇质数,且 \(\omega^p=\omega\cdot(\omega^2)^{(p-1)/2}=\omega\cdot(-1)^{(p-1)/2}=-\omega\),因为 \((a^2-n)^{(p-1)/2}\equiv -1\))。
  • 所以 \((a+\omega)^{p+1}\equiv (a+\omega)(a-\omega)\equiv a^2-(a^2-n)=n\)
  • \(x=(a+\omega)^{(p+1)/2}\) 满足 \(x^2\equiv n\)

例题:P5491 【模板】二次剩余

struct Complex
{
    ll x,y;  // x + y*ω
    Complex(ll _x=0,ll _y=0):x(_x),y(_y){}
};

ll p,w;  // w = a^2 - n

Complex mul(Complex a,Complex b)
{
    ll xx=(a.x*b.x%p+a.y*b.y%p*w%p)%p;
    ll yy=(a.x*b.y%p+a.y*b.x%p)%p;
    return Complex(xx,yy);
}

Complex qpow_c(Complex a,ll b)
{
    Complex res(1,0);
    while(b)
    {
        if(b&1) res=mul(res,a);
        a=mul(a,a);
        b>>=1;
    }
    return res;
}

ll qpow(ll a,ll b)
{
    ll res=1;
    while(b)
    {
        if(b&1) res=res*a%p;
        a=a*a%p;
        b>>=1;
    }
    return res;
}

ll cipolla(ll n,ll p)
{
    n%=p;
    if(n==0) return 0;
    if(qpow(n,(p-1)/2)!=1) return -1;  // 无解
    ll a;
    while(1)
    {
        a=rand()%p;
        w=(a*a%p-n+p)%p;
        if(qpow(w,(p-1)/2)==p-1) break;
    }
    Complex res=qpow_c(Complex(a,1),(p+1)/2);
    return res.x%p;
}

十二、离散对数与 BSGS

12.1 BSGS 算法(大步小步法)

问题:求解 \(a^x\equiv b\pmod p\),其中 \(\gcd(a,p)=1\),求最小非负整数 \(x\)

算法原理
\(x=im-j\),其中 \(m=\lceil\sqrt{p}\rceil\)\(0\le j<m\),则方程化为:

\[a^{im}\equiv b\cdot a^j\pmod p \]

预处理所有 \(b\cdot a^j\) 存入哈希表,然后枚举 \(i\) 查找。

步骤

  1. 计算 \(m=\lceil\sqrt{p}\rceil\)
  2. 预处理 \(b\cdot a^j\ (0\le j<m)\) 的值与对应的 \(j\),存入哈希表(若有冲突取最大 \(j\))。
  3. 计算 \(t=a^m\bmod p\),令 \(cur=t\)
  4. 枚举 \(i=1..m\),检查 \(cur\) 是否在哈希表中,若找到 \(j\),则 \(x=im-j\) 即为解。

复杂度\(O(\sqrt{p})\) 时间,\(O(\sqrt{p})\) 空间。

12.2 扩展 BSGS

\(\gcd(a,p)\neq1\) 时,需处理。设 \(d=\gcd(a,p)\),若 \(d\nmid b\) 则无解;否则两边除以 \(d\),得到新方程,重复直到互质。最后用 BSGS 求解。

推导
\(a^x\equiv b\pmod p\),令 \(d=\gcd(a,p)\)

  • \(d=1\),直接 BSGS。
  • \(d>1\),若 \(b\not\equiv0\pmod d\) 则无解;否则令 \(a'=a/d,\ b'=b/d,\ p'=p/d\),方程化为 \(a^{x-1}\cdot a'\equiv b'\pmod{p'}\),即 \(a^{x-1}\equiv b'\cdot (a')^{-1}\pmod{p'}\),递归处理。

例题:P3846 [TJOI2007] 可爱的质数(BSGS 模板)

ll BSGS(ll a,ll b,ll p)
{
    a%=p; b%=p;
    if(b==1||p==1) return 0;
    unordered_map<ll,ll> mp;
    ll m=ceil(sqrt(p)),t=1;
    for(int j=0;j<m;j++)
    {
        if(!mp.count(t)) mp[t]=j;
        t=t*a%p;
    }
    ll base=qpow(a,p-1-m,p),cur=b;
    for(int i=0;i<m;i++)
    {
        if(mp.count(cur)) return i*m+mp[cur];
        cur=cur*base%p;
    }
    return -1;
}
posted @ 2026-03-07 14:11  Augustus_Deception  阅读(3)  评论(0)    收藏  举报