[总结]数学专题(持续更新)

质因数分解&约数

我们知道可以对一个数进行质因数分解,即

x=pa11pa22...pann

对一个数进行质因数分解后:
约数个数和:
i=1n(ai+1)

约数和:
i=1nj=0aipji


最大公约数与最小公倍数

求最大公约数的方法是经典的欧几里得算法
Code:

int gcd(int a,int b)
{
    return b==0?a:gcd(b,a%b);
}

最大公约数和最小公倍数的关系:

lcm(i,j)=ijgcd(i,j)


扩展欧几里德算法

Code:

void ex_gcd(int a,int b,int &x,int &y)
{
    if (b==0) {x=1; y=0; return;}
    ex_gcd(b,a%b,x,y);
    int t=x; x=y; y=t-a/b*y;
}

利用扩展欧几里德算法可以解不定方程 ax+by=n

  1. 首先令 d=gcd(a,b) ,若 dn 则有解,否则无解
  2. a,b,n 都除以 d ,得到新方程a0x+b0y=n0,此时 gcd(a0,b0)=1
  3. 利用扩展欧几里德算法求出方程 a0x+b0y=1 的一组解 x0,y0 ,则 x0n0,y0n0 是原方程的一组解。
  4. 根据相关定理,方程 a0x+b0y=n0 的所有解为
    x=x0n0+tb0,y=y0n0ta0,tZ

模线性方程 axb (mod p) 可以转化为 ax+py=b 的形式,也可以用上述方法解决。


快速幂

二分的思想.
Code:

inline int quick_power(int a,int b)
{
    int ans=1;
    for (int i=b;i;i>>=1,a=a*a%P)
        if (i&1) ans=ans*a%P;
    return ans;
}

快速乘

二进制的思想,防止直接乘法爆掉.
Code:

int mul(int a,int b)
{
    int ans=0;
    for (int i=b;i;i>>=1,a=(a<<1)%p)
        if (i&1) ans=(ans+a)%p;
    return ans;
}

线性筛素数

素数即只有1和自己两个约数的数。
线性筛素数Code:

inline void get_prime()
{
    for (int i=2;i<=n;i++)
    {
        if (!flag[i]) prime[++prime[0]]=i,pos[i]=prime[0];
        for (int j=1;j<=prime[0]&&i*prime[j]<=n;j++)
        {
            flag[i*prime[j]]=1; pos[i*prime[j]]=j;
            if (i%prime[j]==0) break;
        }
    }
}

这个过程的复杂度是 O(n) 的,重点在于

if  (i%prime[j]==0)  break;

上文中我们知道了可以对一个数进行 质因数分解
也就是说每个数都存在一个最小质因子 p ,保证复杂度为线性的原因就是每个数只会被它的最小质因子p筛去一次,让我们举一个例子:
比如当前 i=4 , i%prime[1]=0(prime[1]=2,prime[2]=3) ,那么此时将 iprime[2] 8 标记为合数,而iprime[3] 12 不会在本次循环中被标记,因为 12 的最小质因子为 2 ,当i=6时, 12 才会被筛去。
知道了线性的原因,我们也可以很方便的找出每个数的最小质因子。


欧拉函数

线性筛欧拉函数Code:

inline void get_phi()
{
    phi[1]=1;
    for (int i=2;i<=n;i++)
    {
        if (!flag[i]) {prime[++prime[0]]=i; phi[i]=i-1;}
        for (int j=1;j<=prime[0]&&i*prime[j]<=n;j++)
        {
            flag[i*prime[j]]=1;
            if (i%prime[j]==0) {phi[i*prime[j]]=phi[i]*prime[j]; break;}
            else phi[i*prime[j]]=phi[i]*(prime[j]-1);
        }
    }
}

欧拉函数是小于 n 的数中与n**互质**的数的数目,我们用φ(n)来表示。
依旧是先进行质因数分解。
通式:

φ(n)=n(11p1)(11p2)(11pn)

这个可以用定义+ 容斥原理来证明,此处省略。
特殊: φ(1)=1
一些性质:

  1. n 是质数p k 次幂,则φ(n)=pkpk1=(p1)pk1,因为除了 p 的倍数外,其他数都跟n互质。
  2. 积性函数:若 m,n 互质,则 φ(mn)=φ(m)φ(n)
  3. n 为奇数时,φ(2n)=φ(n)
  4. n 为质数时,φ(n)=n1
  5. n 为质数时,且i%n=0,则 φ(in)=φ(i)n
  6. 在区间 [0,n) 中与 n 互质的数和为φ(n)n2
  7. dnφ(d)=n

线性筛欧拉函数就是根据性质 2,4,5 来进行的。


乘法逆元

定义:满足 ak1 (mod p) k 值就是a关于 p 的乘法逆元,我们不妨用invi来表示 i 的乘法逆元。
ab mod p就等价于 ainvb mod p 。可以解决除法取模的问题。
乘法逆元的求法:

  1. 根据定义,我们可以列出不定方程 ax+py=1 ,然后用扩展欧几里德算法解决。
  2. 根据费马小定理 ap11 (mod p) ,那么因为 aap2=ap11 (mod p) ,所以在 p 是质数时,a关于 p 的逆元为ap2
  3. 递推求逆元,复杂度为 O(n) ,附代码,证明略
    inv[1]=1;
    for (int i=2;i<MAXN;i++) inv[i]=(p-p/i)*inv[p%i]%p;

以上为乘法逆元常用的 3 种求法。


高斯消元

高斯消元就是不断地消元然后回代,可用来解线性方程组,复杂度为O(n3)
Code:

void gauss()
{
    int y;
    double t;
    for (int i=1;i<=n;i++)
    {
        for (y=i;y<=n;y++) 
            if (fabs(a[y][i])>eps) break;
        if (y>n) continue;
        if (y!=i) 
            for (int j=1;j<=n+1;j++) swap(a[i][j],a[y][j]);
        t=a[i][i];
        for (int j=1;j<=n+1;j++) a[i][j]/=t;
        for (int j=1;j<=n;j++)
            if (j!=i)
            {
                t=a[j][i];
                for (int k=1;k<=n+1;k++)
                    a[j][k]-=t*a[i][k];
            }
    }
}

高斯消元还可以用来解异或方程组,然后引入一个概念叫做自由元:在消元后若 fi,i=0 ,证明 i 是一个自由元,即i的取值不同会造成多解问题,在异或方程组中,由于取值只有 2 种,所以如果有k个自由元,解的个数为 2k 。常用枚举自由元的方式来计算不同的解。
Code:

inline void gauss()
{
    int y;
    for (int i=1;i<=n;i++)
    {
        for (y=i;y<=n;y++)
            if (a[y][i]) break;
        if (y>n) continue;
        if (i!=y) 
            for (int j=1;j<=n+1;j++) swap(a[i][j],a[y][j]);
        for (int j=1;j<=n;j++)
            if (j!=i&&a[j][i])
                for (int k=1;k<=n+1;k++) a[j][k]^=a[i][k];
    }
}
void dfs(int x)
{
    if (tot>=mn) return;
    if (!x) 
    {
        mn=min(mn,tot);
        return;
    }
    if (a[x][x])
    {
        int t=a[x][n+1];
        for (int i=x+1;i<=n;i++)
            if (a[x][i]) t^=ans[i];
        ans[x]=t;
        if (t) tot++;
        dfs(x-1);
        if (t) tot--;
    }
    else
    {
        ans[x]=0; dfs(x-1);
        ans[x]=1; tot++; dfs(x-1); tot--;
    }
}

Lucas定理

Lucas 定理是用来解决大组合数取模的。
即求 Cmn mod p ,其中 p 为质数。
Cmn % p=Cm/pn/pCm%pn%p % p

莫比乌斯反演

线性筛莫比乌斯函数Code:

inline void prepare()
{
    mobius[1]=1;
    for (int i=2;i<MAXN;i++)
    {
        if (!flag[i]) prime[++prime[0]]=i,mobius[i]=-1;
        for (int j=1;j<=prime[0]&&i*prime[j]<MAXN;j++)
        {
            flag[i*prime[j]]=1;
            if (i%prime[j]==0)
            {
                mobius[i*prime[j]]=0;
                break;
            }
            mobius[i*prime[j]]=-mobius[i];
        }
    }
}

参考资料:WC2016第二课堂宋新波讲稿

我们定义两个函数 f(n) g(n) ,并且满足 f(n)=dng(d) ,根据定义:
f(1)=g(1)
f(2)=g(1)+g(2)
f(3)=g(1)+g(3)
f(4)=g(1)+g(2)+g(4)
f(5)=g(1)+g(5)
f(6)=g(1)+g(2)+g(3)+g(6)
f(7)=g(7)
f(8)=g(1)+g(2)+g(4)+g(8)
于是我们可以得到:
g(1)=f(1)
g(2)=f(2)f(1)
g(3)=f(3)f(1)
g(4)=f(4)f(2)
g(5)=f(5)f(1)
g(6)=f(6)f(3)f(2)+f(1)
g(7)=f(7)f(1)
g(8)=f(8)f(4)

我们发现了什么?
g(n)=dnf(d)×?
? 部分与d似乎没有什么关系,而与 nd 有关系。
我们定义一个新的函数,记为 μ(i)
g(n)=dnf(d)×μ(nd)
或者通过换元可以写成 g(n)=dnf(nd)×μ(d)
我们得到了公式:
f(n)=dng(d)=>g(n)=dnf(nd)×μ(d)

莫比乌斯函数的定义
μ(d) 为莫比乌斯函数,定义如下:
①若 d=1 ,则 μ(d)=1
②若 d=P1×P2×...×Pn ,则 μ(d)=(1)k
③其他情况 μ(d)=0

莫比乌斯函数的性质
①若 n=1 ,则 dnμ(d)=1 ,否则 dnμ(d)=0 .
可以用二项式定理来证明。
②莫比乌斯函数是积性函数

莫比乌斯反演的证明
证明: f(n)=dng(d)=>g(n)=dnf(nd)×μ(d)

g(n)=dnf(nd)μ(d)
=dnμ(d)indg(i)
=ing(i)dniμ(d)
①当 i=n 时, dniμ(d)=1 , g(i)dniμ(d)=g(n)
②当 i 取其他值时,dniμ(d)=0 g(i)dniμ(d)=0
综上, g(n)=dnf(nd)μ(d)=g(n)

莫比乌斯反演的变形
f(i)=id,dng(d)=>g(i)=id,dnf(d)μ(di)
其他写法: f(i)=nid=1g(di)=>g(i)=nid=1f(di)μ(d)
证明与莫比乌斯反演的证明类似,在此不赘述。

莫比乌斯反演的性质
f(n)=dng(d) ,则“ g(n) 是积性函数”的充分必要条件是“ f(n) 是积性函数”。

常用技巧
变量替换,内层外移。
gcd(i,j)=1 等价于 dgcd(i,j)μ(d)

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值