1010 字
5 分钟
3-1数论

数论#

目录

欧拉筛(经典版本) 欧拉筛(最下质数筛) 质因子分解(O(n)筛法,O(logn)质因子分解 ) 矩阵的转置 (横向变纵向,纵向变横向)

欧拉筛(经典版本)#

vector<int> pri;
void pri_init(int n)
{
vector<bool> is_pri(n + 1, true);
is_pri[0] = is_pri[1] = false;
for (int i = 2; i <= n; i++)
{
if (is_pri[i]) pri.push_back(i);
for (int j = 0; j < pri.size() && pri[j] <= n / i; j++)
{
is_pri[pri[j] * i] = false;
if (i % pri[j] == 0) break;
}
}
}

欧拉筛(最小质数筛)#

vector<int> pri;
int pmin[N];
void my_pmin(int n)
{
for (int i = 2; i <= n; i++)
{
if (pmin[i]==0)
{
pmin[i] = i;
pri.push_back(i);
}
for (auto p : pri)
{
if (i * p > n) break;
pmin[i * p] = p;
if (i % p == 0) break;
}
}
}

质因子分解(O(n)筛法,O(logn)质因子分解 )#

质因子分解解释#

✅ 结论公式:

设一个正整数 ( n ) 的 质因子分解 为:

[ n = p_1^{a_1} \cdot p_2^{a_2} \cdot \cdots \cdot p_k^{a_k} ]

那么它的约数个数(包括 1 和 ( n ) 本身)为:

[ \text{约数个数} = (a_1 + 1)(a_2 + 1) \cdots (a_k + 1) ]

vector<int> pri;
int pmin[N];
void my_pmin(int n)
{
for (int i = 2; i <= n; i++)
{
if (!pmin[i])
{
pmin[i] = i; // pmin[i]:i 的最小质因子
pri.push_back(i);
}
for (auto p : pri)
{
if (i * p > n) break; // 超范围了
pmin[i * p] = p;
if (i % p == 0) break;
}
}
}
ve<pii> nszka(int n)
{
map<int, int> mp;
while (n != 1)
{
int pm = pmin[n];
mp[pm]++;
n /= pm;
}
ve<pii> f;
for (auto [x, y] : mp) f.push_back({x, y});
return f;
}
// 示例:分解 60 -> [(2,2), (3,1), (5,1)]

矩阵的转置 (横向变纵向,纵向变横向)#

ve<ve<int>>a(n+1,ve<int>(n+1));
ve<ve<int>>b(n+1,ve<int>(n+1));
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
b[i][j]=a[j][n-i+1];
// cout<<"("<<i<<", "<<j<<") ("<<j<<", "<<n-i+1<<") \n";
}
}
a=b;

组合数( 快速幂+组合数+逆元预处理 O(n) )#

✅ 问题形式:(能使用乘法逆元的前提)#

要求的是:

[ \frac{a}{b} \mod m ]


若:

[ \gcd(b, m) = 1 ]

则 ( b ) 在模 ( m ) 意义下有乘法逆元 ( b^{-1} \mod m ),于是可以转化为:

[ \frac{a}{b} \mod m \equiv a \cdot b^{-1} \mod m ]

int ksm(int a, int n)
{
int ans = 1;
while (n)
{
if (n & 1) ans = ans * a % mod;
a = a * a % mod; n >>= 1;
}
return ans;
}
struct ZHS
{
int fac[N+2], ifac[N+2];
ZHS() { init(); }
void init()
{
fac[0] = ifac[0] = 1;
for (int i = 1; i <= N; i++) fac[i] = fac[i - 1] * i % mod;
ifac[N] = ksm(fac[N], mod - 2);
for (int i = N - 1; i >= 1; i--) ifac[i] = ifac[i + 1] * (i + 1) % mod;
}
int C(int n, int m)
{
if (n >= m && m >= 0 && N > n)
return fac[n] * ifac[m] % mod * ifac[n - m] % mod;
return 0;
}
int A(int n, int m)
{
if (n >= m && m >= 0 && N > n)
return fac[n] * ifac[n - m] % mod;
return 0;
}
} zhs;
int inv(int a,int mod) { return ksm(a,mod-2,mod);}

扩展欧几里得exgcd#

求解形如 ax+by==gcd(a,b) 的不定方程的任意一组解

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

杨辉三角#

杨辉三角模板题#

杨辉三角具有如下的初始条件:

Ci,0=Ci,i=1C_{i,0} = C_{i,i} = 1

杨辉三角有如下的递推公式:

Ci,j=Ci1,j+Ci1,j1C_{i,j} = C_{i-1,j} + C_{i-1,j-1}


杨辉三角本质是二项式系数,其第 nn 行各个数值即为 (a+b)n(a+b)^n 展开后各项的系数。
展开后共有 n+1n+1 项,第 ii 项的系数记作 Cn,iC_{n,i},形如:

Cn,0an+Cn,1an1b+Cn,2an2b2++Cn,n1abn1+Cn,nbnC_{n,0}a^n + C_{n,1}a^{n-1}b + C_{n,2}a^{n-2}b^2 + \dots + C_{n,n-1}ab^{n-1} + C_{n,n}b^n

由于 ana^n 只能由 nn(a+b)(a+b)aa 相乘得到,因此其系数为 11bnb^n 同理,由此得到了杨辉三角的边界条件式。

对于杨辉三角第 nn 行中第 iiCn,iC_{n,i},其为 aibnia^i b^{n-i} 的系数。
考虑第 nn 行的 aibnia^i b^{n-i} 可能来源:

(a+b)n=(a+b)n1(a+b)(a+b)^n = (a+b)^{n-1}(a+b)

即:

(Cn1,0an1+Cn1,1an2b++Cn1,n1bn1)(a+b)(C_{n-1,0}a^{n-1} + C_{n-1,1}a^{n-2}b + \dots + C_{n-1,n-1}b^{n-1})(a+b)

于是 aibnia^i b^{n-i} 可以由上一行的 ai1bnia^{i-1}b^{n-i} 乘以 aa,或者由 aibni1a^i b^{n-i-1} 乘以 bb 得到。
由此可得:

Cn,i=Cn1,i1+Cn1,iC_{n,i} = C_{n-1,i-1} + C_{n-1,i}

即杨辉三角的递推式。


由于二项式系数的组合性质,杨辉三角的 Ci,jC_{i,j} 项即为组合数 C(i,j)C(i,j),这使得杨辉三角成为时间复杂度允许情况下一种简洁好写的组合数求法。


#include <cstdio>
int N,C[30][30];
int main()
{
scanf("%d",&N);
int i,j;
C[0][0]=1;printf("1\n");
for(i=1;i<=N;i++)
{
for(j=1;j<=i;j++)
{
C[i][j]=C[i-1][j]+C[i-1][j-1];
printf(j!=i?"%d ":"%d\n",C[i][j]);
}
}
}
3-1数论
https://fuwari.vercel.app/posts/3-1数论/
作者
nszkay
发布于
2026-01-07
许可协议
CC BY-NC-SA 4.0