复习:数论


质数

判断质数

朴素算法

考虑一个数\(x\),其一定能分解成\(a*b\)的形式,这里只需要保证\(a\)\(b\)都为整数即可,

显然的,\(min(a,b)\le \sqrt{x}\),所以只需要枚举\([2,\sqrt x]\)即可

bool pd(int x)
{
    for(int i=2;i*i<=x;i++)
        if(x%i==0)
            return 0;
    return 1;
}

Miller_Rabin素性测试

内容

首先根据费马定理

随便取一个数\(a\)

如果\(p\)为质数,那么就有\(a^{p-1}\equiv 1 \pmod p\),但是反过来不成立

具体的,卡迈克尔数

所以我们需要用到二次探测方法

我们将\(p-1\)分解成为\(2^r*k\),保证有\(2\not| \frac{p-1}{2^r}\),则有\(a^{p-1}=a^{{k}^{2r}}\)

先求出\(a^k\),再不断平方,如果第一次模\(p\)的值为1,那么检测上一次的值是否为\(-1\),如果是那么就是质数

考虑如果有\(y^2\%p=1\)

那么就有\((y-1)(y+1)=kp\)

如果\(p\)为质数,那么一定有

\(y-1=0或者y+1=p\)

所以\(y\)的值只能是\(1\)或者\(p- 1\)

考虑如果有\(r\)次平方之后依然没有出现余数为\(1\)的情况,那么显然\(p\)不为质数

但是通过二次探测,我们依然不能百分之百的确定\(p\)是不是质数,但是出错的概率非常小,基于此,如果多来几次,则可以接近百分之百的概率了

这里建议\(a\)选择质数

实现

bool f=1;
for(int i=1;i<=lenp;i++)
{  
	if(mod%pri[i]==0)
		continue;
    if(qkpow(pri[i],mod-1,mod)!=1&&pri[i]%mod!=0)
    {
        f=0;
        break;
    }
    int cnt=0;
    long long t=mod-1;
    while(t%2==0)
    {
        t/=2;
        cnt++;
    }
    long long now=qkpow(pri[i],t,mod);
    long long las=now;
    if(now==1)
    	continue;
    for(int j=1;j<=cnt;j++)
    {
        now=qkpow(now,2,mod);
        if(now==1)
        {
            if(las!=mod-1)
                f=0;
            break;
        }
        las=now;
    } 
    if(now!=1)
    	f=0;
}
if(f)
    cout<<"It is a prime number.\n";
else
    cout<<"It is not a prime number.\n";

区间内的质数

埃氏筛法

一个自然数集合

考虑到质数的因数只有\(1\)和他本身,所以考虑对每一个数,将其在集合中的所有的倍数都删去,没有被删去的数即为质数,时间复杂度即为\(\sum_{i=1}^{n}\frac{n}{i}=n*log_n\)(调和级数求和),也可以看做每个数被筛了多少次

bool vis[MAXN+5];
int pri[MAXN],lenp;
void sieve(int n)
{
    for(int i=2;i<=n;i++)
    {
        if(vis[i]==0)
        {
            pri[++lenp]=i;
            for(int j=i;1ll*i*j<=n;j++)
                vis[i*j]=1;
        }
    }
}

欧拉筛

考虑在埃氏筛上进行改进,为什么埃氏筛会带一个log,因为其会被其所有的质因数都筛一次

我们对其进行一些限制,每一个数只能被其最小的质因数筛去

考虑目前的质数集合为\(P\),我们枚举其的倍数\(a\)

从小到大枚举\(P\)中的元素

如果\(a\%p_i==0\),那么比\(p_i\)大的元素可以不用枚举,设\(a=k*p_i\),那么\(a*p_{i+1}=p_i*k*p_{i+1}\),根据我们所规定的限制,那么\(a*p_{i+1}\)一定会被\(p_i\)筛去,

bool vis[MAXN+5];
int pri[MAXN],lenp;
void sieve(int n)
{
    for(int i=2;i<=n;i++)
    {
        if(vis[i]==0)
            pri[++lenp]=i;
        for(int j=1;j<=lenp&&1ll*i*pri[j]<=n;j++)
        {
            vis[i*pri[j]]=1;
            if(i%pri[j]==0)
                break;
        }
    }
}

最大公约数和最小公倍数

辗转相除法

内容

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

证明

\(gcd(a,b)=p\),那么\(\begin{cases}a=x*p\\b=y*p\end{cases}\)\(a\%b=(x*p)\%(y*p)=x\%y*p\)

\(b\)\(a\%b\)的最大公约数依然为\(p\)

实现

其实可以直接用\(algorithm\)库中的__gcd

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

裴蜀定理

内容

如果有\(gcd(a,b)=d\),且有\(c|d\),那么一定有整数\(x,y\),满足\(ax+by=c\)

证明

如果有\(ax+by=c\),那么一定有\(ax*\frac{d}{c}+by*\frac{d}{c}=d\),设\(x'=x*\frac{d}{c},y'=y*\frac{d}{c}\),此时的\(x'和y'\)一定都为整数,那么\(ax'+by'=d\),因为\(gcd(a,b)=d\),所以有\(\frac{a}{d}x'+\frac{b}{d}y'=1\)。同样的,设\(a'=\frac{a}{d},b'=\frac{b}{d}\),所以\(a'x'+b'y'=1\)

我们要证明的既可以转换为:对于一对\(a,b\),满足\(gcd(a,b)=1\),一定有整数\(x,y\),满足\(ax+by=1\)

引理1

如果有\(gcd(a,b)=1\),那么不存在一个小于b的正整数k,使得\(k*a\equiv 0\pmod b\)

反证法,

如果存在一个\(k\)使得\(k*a\equiv 0 \pmod b\),那么\(k*a\)中一定有\(b\)的所有质因数,但同时\(a\)没有任何一个\(b\)的质因数,所以\(k\)一定包含\(b\)的所有质因数,即\(k\ge b\),矛盾,所以引理得证

推论

在相同的条件下,\(0*a\%b,1*a\%b\ldots(b-1)*a\%b\),这些数一定都不一样

反证法,

\(i*a\equiv j*a\pmod b,同时0<i<j<b\),那么一定有\(a*(j-i)\equiv 0 \pmod b\)

根据引理1,\(j-i\ge b\),显然这是矛盾的

引理2

如果有\(gcd(a,b)=1\),必然存在一个整数\(k\),使得\(k*a\%b=1\)

根据推论,对于\(0*a\%b,1*a\%b\ldots(b-1)*a\%b\),这些数一定不一样,但是某一个数取模\(b\)的结果一定在\([0,b-1]\),但是同时根据推论,我们已经可以得到\(b\)个两两不相同的数,所以k一定存在

再来考虑如果有\(gcd(a,b)=1\),那么一定有\(x*a\%b=1\),所以定然有\(ax-by=1\),所以有\(ax+by=1\),即裴蜀定理得证

同余等价系,剩余系,缩系

对于一个正整数\(p\),对于\(a\%p\),只可能有\(p\)种结果,并且属于\([0,p-1]\)

\(p\)的剩余系记作\(Z_p\),即小于\(p\)的所有非负整数

同余等价类,

考虑在模\(p\)的剩余系下,只有\(p\)种结果,所以每一个数都代表了\(\%p=a\)的所有数,对于\(a\)相同的数,我们称其为同余等价类

如果只考虑\(gcd(i,p)=1\)的元素,便得到一个子集,称这个子集为缩系,记作\(Z^*_p\)

欧拉函数

内容

\(p\)得缩系的大小,记作\(\phi(p)\)

如果\(p\)为质数,那么\(\phi(p)=p-1\)

如果\(n=p^q\),这里的\(p\)为质数,那么\(\phi(n)=p^q-p^{q-1}=p^q*(1-\frac{1}{p})\)

同时欧拉函数是一个积性函数证明

所以\(\phi(n)=n*\prod_{p_i|n,并且p_i为质数}(1-\frac{1}{p_i})\)

求单个\(\phi\)

直接套用公式即可

int phi(int n)
{
    int ret=n;
    for(int i=2;1ll*i*i<=n;i++)
    {
        if(n%i==0)
        {
            ret=ret/i*(i-1);
            while(n%i==0)
                n=n/i;
        }
    }
    if(n>1)
        ret=ret/n*(n-1);
    return ret;
}

求区间每个数的\(\phi\)

因为\(\phi\)为积性函数,所以只需要改进一下埃氏筛法即可,

当然也可以改欧拉筛,但是改后的时间复杂度变成了\(O(n*log_n)\)可能是笔者太菜,想不到更好的改法

void sieve(int n)
{
    for(int i=1;i<=n;i++)
        s[i]=i;
    for(int i=2;i<=n;i++)
    {
        if(vis[i]==0)
        {
            for(int j=1;1ll*i*j<=n;j++)
            {
                vis[i*j]=1;
                s[i*j]=s[i*j]/i*(i-1);
            }
        }
    }
}

欧拉定理

内容

如果有\(gcd(a,p)=1\),那么有\(a^{\phi(p)}\%p=1\)

证明

\(p\)的缩系为\(\{p_1,p_2,\ldots,p_n\}\),因为\(gcd(a,p)=1\),所以\(\{a*p_1\%p,a*p_2\%p,\ldots,a*p_n\%p\}\)同样也是\(p\)的缩系

所以有\(\sum_{i=1}^{n}p_i\%p=\sum_{i=1}^{n}a*p_i\%p=\sum_{i=1}^{n}p_i\%p*a^{\phi(p)}\%p\)

\(a^{\phi(p)}\%p=1\)

拓展欧拉定理

内容

\(a,m\)为正整数,当\(r>\phi(m)\),有\(a^r\equiv a^{r\%\phi(m)+\phi(m)}\pmod m\)

证明

考虑如果\(gcd(a,m)=1\),这个结论显然成立

所以讨论\(gcd(a,m)\neq1\)的情况

引理1

如果有\(\begin{cases}x\equiv y\pmod n\\x\equiv y\pmod m\end{cases}\),那么有\(x\equiv y \pmod {lcm(n,m)}\)

比较明显?

\(\begin{cases}x-y\equiv 0\pmod n\\x-y\equiv 0 \pmod m\end{cases}\rightarrow x-y\equiv 0 \pmod {lcm(n,m)}\rightarrow x\equiv y \pmod {lcm(n,m)}\)

引理2

如果\(p\)为质数,\(\phi(p^q)\ge q\)

显然的\(gcd(p^i-1,p^1)==1\),并且\(i\in [1,q]\),所以\(\phi(p^q)\ge q\)

引理3

如果\(m=p^q\)\(p\)为质数,那么有\(a^r\equiv a^{r\%\phi(m)+\phi(m)}\equiv 0\pmod m\)

因为\((a,m)>1\),则\(a\)必然有因数\(p\)

因为\(r>\phi(m)\ge q\),所以有\(a^r=x*p^r=y*p^{\phi(m)}=z*p^{q}\)

因为\(r>q\),所以\(a^r\equiv 0 \pmod m\)

因为\(\phi(m)\ge q\),所以\(r\%\phi(m)+\phi(m)\ge q\),所以\(a^{r\%\phi(m)+\phi(m)}\equiv 0\pmod m\)

所以\(a^r\equiv a^{r\%\phi(m)+\phi(m)}\equiv 0\pmod m\)

现在考虑\(m=\prod p_i^{q_i}\)

\(m_i=p_i^{q_i}\)

显然,对于每一个\(m_i\)

如果\(gcd(a,m_i)=1\),则根据欧拉定理可以得到\(a^r\equiv a^{r\%\phi(m)+\phi(m)}\pmod {m_i}\)

如果\(gcd(a,m_i)>1\),则根据引理3有\(a^r\equiv a^{r\%\phi(m)+\phi(m)}\pmod {m_i}\)

因为欧拉函数是一个积性函数并且\(m_i\)之间两两互质,所以\(\phi(m)=\prod \phi(m_i)\)

根据引理1可以得

\(a^r\equiv a^{r\%\phi(m)+\phi(m)}\pmod m\)

逆元

当整数\(a*b\equiv 1 \pmod p\),我们称\(b\)为a的逆元,记作\(b=a^{-1}\)

只有\(a\)\(p\)互质的情况下,\(a\)才有逆元

费马定理

内容

如果\(p\)为质数,且\(a\%p\neq 0\),则有\(a^{p-1}\%p=1\)

证明

考虑到裴蜀定理的推论,对于\(i\in[1,p-1]\)\(gcd(i*a,p)\)一定是互质的,

即有\(\sum_{i=1}^{p-1}i*a\%p=\sum_{i=1}^{p-1}i\%p\)

又知道\(\sum_{i=1}^{p-1}i*a\%p=a^{p-1}\%p*\sum_{i=1}^{p-1}i\%p\)

即有\(a^{p-1}\%p=1\)

威尔逊定理

内容

\((p-1)!\equiv -1\pmod p\),当且仅当\(p\)为质数,其中\((p-1)!=\prod_{i=1}^{p-1}i\)

证明

充分性

\(p\)为质数\(\rightarrow (p-1)!\equiv -1 \pmod p\)

因为\(i\in [1,p-1]\),且\(p\)为质数,所以一定有\(gcd(i,p)=1\),根据裴蜀定理的引理2,一定存在\(j*a\%p=1\)\(j\in[1,p-1]\),同时,根据裴蜀定理的引理1,\(i\)只有1个逆元,

再者,我们考虑\(j=i\)的情况

\(i^2\%p=1\rightarrow i^2-1=kp\)

\((i-1)(i+1)=kp\),同时\(p\)为质数,且\(i\in[1,p-1]\)

\(i=1,p-1\)

即除了1和p-1,其他数都有一个唯一的逆元,同时我们根据欧拉定理$a^{-1}\equiv a^{p-2}\pmod p $,再根据裴蜀定理的引理1,每个数的逆元都是不一样的(反证法)

所以一定有$(p-1)!\equiv -1 \pmod p $

必要性

\((p-1)!\equiv -1 \pmod p \rightarrow\) \(p\)为质数

\(p|(p-1)!+1\)

假设\(p\)不是质数,且\(a\)\(p\)的质因子

那么一定有\(a|(p-1)!+1\)

但是同时,因为\(a\)\(p\)的一个质因子,所以一定有\(a|(p-1)!\)

但同时\(gcd((p-1)!+1,(p-1)!)=1\),故\(a\)只可能为1,

与假设矛盾,所以不成立,即命题得证

拓展欧几里得

内容

求不定方程\(ax+by=c\),其中\(a,b,c\)已知,\(gcd(a,b)|c\)

如果我们已知方程\(ax+by=gcd(a,b)\),那么\(ax+by=c\)也就是在原来的基础上乘上\(\frac{c}{gcd(a,b)}\)

\(\begin{aligned}ax+by&=gcd(a,b)\\&=gcd(b,a\%b)\\&=bx'+(a\%b)y'\\&=bx'+(a-b*\lfloor\frac{a}{b}\rfloor)y'\\&=ay'+b(x'-y'\lfloor\frac{a}{b}\rfloor)\end{aligned}\)

\(\begin{cases}x=y'\\y=x'-y'\lfloor\frac{a}{b}\rfloor\end{cases}\)

考虑一直递归下去,则一定会出现\(gcd(a,0)=a\)的情况

即此时\(\begin{cases}x=1\\y=0\end{cases}\)

实现

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

单个逆元

费马

内容

\(p\)为质数的情况下

根据费马定理\(a^{p-1}\equiv 1\pmod p\),所以\(a^{p-2}\equiv a^{-1}\pmod p\)

如果\(p\)不是质数

考虑对\(ax\%p=1\),那么就一定有\(ax-py=1\),设\(y'=-y\),则有\(ax+py'=1\)

这里因为\(a\)\(p\)互质,所以方程一定有解,通过拓展欧几里得算法即可求出\(x\)

实现

不会有人连快速幂都不会吧,不会吧不会吧

int qkpow(int a,int b)
{
    if(b==0)
        return 1;
    if(b==1)
        return a;
    long long t=qkpow(a,b/2);
    t=t*t%mod;
    if(b%2==1)
        t=t*a%mod;
    return t;
}

拓展欧几里得

内容

显然的,有方程\(ia\equiv 1 \pmod p\)

考虑对方程进行变形\(ia -1=jp\)

\(ia-jp=1\)

这个时候直接用exgcd来解就可以了

实现

void exgcd(long long a,long long b,long long &x,long long &y)
{
    if(b==0)
    {
        x=1;
        y=0;
        return;
    }
    else
    {
        exgcd(b,a%b,y,x);
        y=y-x*(a/b);
    }
}
long long inv(long long a,long long b)
{
    long long x,y;
    exgcd(a,b,x,y);
    return (x%b+b)%b;
}

区间逆元

递推式

关于逆元,有一个很巧妙的递推式

\(f(i)\)表示\(i\)在模\(m\)意义下的逆元

边界条件即为\(f(1)=1\)

那么\(f(n)=(-f(m\%n)*(m/n)\%m+m)\%m\)

证明

我们设\(m=k*i+r\)\(k=\lfloor\frac{p}{m}\rfloor,r=m\%n\)

那么\(k*i+r\equiv 0\pmod m\)

那么有\((k*i+r)*i^{-1}*r^{-1}\equiv 0*i^{-1}*r^{-1}\pmod m\)

\(k*r^{-1}+i^{-1}\equiv 0\pmod m\)

\(k*r^{-1}\equiv -i^{-1}\pmod m\)

\(-k*r^{-1}\equiv i^{-1}\pmod m\)

即递推式得证

中国剩余定理

非拓展

内容

求解方程组\(\begin{cases}x\equiv a_1\pmod {r_1}\\x\equiv a_2\pmod {r_2}\\\dots\\x\equiv a_n\pmod {r_n}\end{cases}\)保证\(r_i\)两两互质

解法

我们设\(A_i=\prod_{j=1}^n r_j[i\neq j]\),因为\(r_i\)之间两两互质,所以\(gcd(A_i,r_i)=1\),所以必然存在\(c_i*A_i\%r_i=1\)(裴蜀定理的引理2),设\(x_i=a_i*A_i*c_i\),则\(x_i\%r_i=a_i\),且\(x_i\%r_j=0(i\neq j)\)

所以最后的\(x=\sum_{i=1}^{n}a_i*A_i*c_i\%r\)

通解的话就相当与\(x+\prod_{i=1}^nr_i\)

实现

long long crt()
{
    long long s=1,x=0;
    for(int i=1;i<=n;i++)
        s=s*r[i];
    for(int i=1;i<=n;i++)
        x=(x+(a[i]*(s/r[i])%s*inv(s/r[i],r[i]))%s)%s;
    return x;
}

拓展

内容

总有一些恶心的出题人,将\(r_i\)两两互质的条件删去,但依然需要我们求解方程组\(\begin{cases}x\equiv a_1\pmod {r_1}\\x\equiv a_2\pmod {r_2}\\\dots\\x\equiv a_n\pmod {r_n}\end{cases}\)

解法

考虑取出前两个方程,即有\(\begin{cases}x=r_1*k_1+a_1\\x=r_2*k_2+a_2\end{cases}\)

\(r_1*k_1+a_1=r_2*k_2+a_2\)

\(r_1*k_1-r_2*k_2=a_2-a_1\)

这是一个不定方程的形式,可以用exgcd来求解

其中\(k_1\)的通解为\(k_1=k_0+b*\frac{r_2}{gcd(r_1,r_2)}\)\(k_0\)为方程的一组特解

\(k_1\)代回去

\(x=(k_0+b*\frac{r_2}{gcd(r_1,r_2)})*r_1+a_1\)

\(x=k_0*r_1+b*lcm(r_1,r_2)+a_1\)

\(x\equiv k_0*r_1+a_1\pmod {lcm(r_1,r_2)}\)

发现这个方程与方程组中方程的形式的一样的,并且满足这一个方程一定也满足取出来的两个方程

所以最后实际上需要求解的是\(x\equiv a\pmod r\)的一个方程,这把\(x=a\)不就行了

实现

void exgcd(long long a,long long b,long long &x,long long &y)
{
    if(b==0)
    {
        x=1;
        y=0;
        return;
    }
    else
    {
        exgcd(b,a%b,y,x);
        y=y-x*(a/b);
    }
}
long long solve(long long a,long long b,long long c)
{
    long long x,y;
    exgcd(a,b,x,y);
    x=x*c/__gcd(a,b);
    x=x%(b/__gcd(a,b));
    return x;
}
long long excrt()
{
    long long mod;
    for(int i=2;i<=n;i++)
    {
        if((a[i]-a[i-1])%__gcd(r[i],r[i-1])!=0)
            return -1;
        mod=(r[i-1]*r[i])/__gcd(r[i-1],r[i]);
        a[i]=solve(r[i-1],r[i],a[i]-a[i-1])%mod*r[i-1]%mod+a[i-1];
        r[i]=mod;
        a[i]=(a[i]%r[i]+r[i])%r[i];
    }
    return a[n];
}

原根

如果有两个整数\(a\)\(b\)互质,那么根据欧拉定理\(a^{\phi(b)}\%b=1\)

定义模\(b\)意义下\(a\)的阶为使\(a^d\%b=1\)的最小正整数\(d\)

如果\(d=\phi(b)\),那么称\(a\)\(b\)的原根

欧拉证明了,模\(b\)存在原根的充要条件为\(b=2,4,p^n,2p^n\),其中\(p\)为奇质数,\(n\)为任意正整数

原根的数量

如果\(b\)有原根的话,原根的数量为\(\phi(\phi(b))\)

求原根

没有什么特别好的办法,暴力枚举每一个数是不是原根

对于一个数\(a\),如果存在\(a^{\frac{\phi(b)}{p}}\%b=1\),这里的\(p\)\(\phi(b)\)的质因数,则\(a\)不是原根,否则就不是

因为如果有一个数\(a^i\%b=1\),那么\(i|\phi(b)\),并且我们一定会枚举到\(p\)使得\(i|\frac{\phi(b)}{p}\),只需要使得\(gcd(i,p)=1\)即可

大步小步算法(BSGS)

非拓展

内容

考虑有一个同余方程\(a^x\equiv b \pmod p\)\(a,b,p\)已知,\(p\)\(a\)互质,求解\(x\)

解法

我们设\(m=\lceil\sqrt p\rceil,r=x\%m\)

显然的,我们有\(x=km+r\),其中\(k\in[0,\lceil\sqrt p\rceil-1],r\in[0,p-1]\)

\(a^{km+r}\equiv b\pmod p\)

同样的,我们再次变化一下\(k\in[1,\lceil\sqrt p\rceil],r\in[1,m]\)

\(a^{km-r}\equiv b \pmod p\)

\(a^{km}\equiv ba^r\pmod p\)

我们只需要将\(ba^r\%p\)存在一个map中,再枚举\(a^{km}\)即可

实现

long long bsgs(long long a,long long b,long long mod)
{
    long long m=ceil(sqrt(mod));
    long long temp=1;
    for(int i=1;i<=m;i++)
    {
    	temp=temp*a%mod;
      f[b*temp%mod]=i;
	}
	long long ret=temp;
    for(int i=1;i<=m;i++,ret=ret*temp%mod)
        if(f.count(ret))
            return i*m-f[ret];
    return -1;
}

拓展

内容

一样的条件,只是单纯的将\(a\)\(p\)互质这一点去掉

解法

考虑将式子进行变形,那么有\(a^x+kp=b\)

\(gcd(a,p)=g\),显然\(g\not | b\)的时候,方程无解,

但是这里明显有一个例外,当\(b=1\)的时候,\(x=0\)

那么有\(\frac{a}{g}a^{x-1}+k\frac{p}{g}=\frac{b}{g}\)

我们就这么一直迭代下去,设\(a'\)\(\frac{a}{g}\)的乘积,\(p'\)为最后的\(\frac{p}{g}\)\(b'\)为最后的\(\frac{b}{g}\)

假设迭代了\(cnt\)

那么最后的式子的形式是一定\(a^{x-cnt}a'+kp'=b'\)

即为\(a^{x-cnt}a'\equiv b'\pmod {p'}\rightarrow a^{x-cnt}\equiv b'a'^{-1}\pmod {p'}\)

与原式不同的是,这里保证\(gcd(a^{x-cnt},p')=1\)

所以此时就可以直接用非拓展的BSGS来做

显然的,迭代最多迭代\(log\)次,所以总的时间复杂度依然为\(\sqrt n\)

实现

void exgcd(long long a,long long b,long long &x,long long &y)
{
    if(b==0)
    {
        x=1;
        y=0;
        return;
    }
    else
    {
        exgcd(b,a%b,y,x);
        y=y-x*(a/b);
    }
}
long long inv(long long a,long long b)
{
    long long x,y;
    exgcd(a,b,x,y);
    return (x%b+b)%b;
}
long long bsgs(long long a,long long b,long long mod)
{
    f.clear();
    long long m=ceil(sqrt(mod)),temp=1;
    for(int i=1;i<=m;i++)
    {
        temp=temp*a%mod;
        f[temp*b%mod]=i;
    }
    long long ret=temp;
    for(int i=1;i<=m;i++,ret=ret*temp%mod)
        if(f.count(ret))
            return i*m-f[ret];
    return -1;
}
long long exbsgs(long long a,long long b,long long mod)
{
    int cnt=0;
    long long g;
    while(__gcd(a,mod)!=1)
    {
        g=__gcd(a,mod);
        if(b%g!=0)
        {
            if(b==1)
                return cnt;
            else
                return -1;
        }
        cnt++;
        b/=g;
        mod/=g;
        b=b*inv(a/g,mod)%mod;
        if(b==1)
            return cnt;
    }
    return cnt+bsgs(a,b,mod);
}

高斯消元

内容

求解线性方程组\(\begin{cases}i_{11}x_1+i_{12}x_2+\dots+i_{1n}x_n=a_1\\i_{21}x_1+i_{22}x_2+\dots+i_{2n}x_n=a_2\\\dots\\i_{n1}x_1+i_{n2}x_2+\dots+i_{nn}x_n=a_n\end{cases}\)

解法

很明显的有一个暴力的方法,但是这个暴力的方法对于人类来说十分的不方便

那就是,主元法,高斯消元就是这样的

同时\(i\)行的方程,去将\(i+1\)\(n\)行的方程的某一个元消去

那么最后就会得到一个上三角,对于这个上三角,显然的,可以从下往上依次解每一个元,时间复杂度即为\(O(n^3)\)

组合数学

容斥

容斥实际上是考虑改变一些限制条件,使得原来不好算的式子变得好算,最典型的莫过于将恰好\(i\)个改变成为至少\(i\)

举一个例子进行说明传送门

如果还不懂的话,推荐一个巨佬的博客

排列数和组合数

排列数

定义

\(n\)个物品中不放回地依次选\(m\)个物品,考虑顺序,有多少种方案,记作\(A_n^m\)

公式

\(A_n^m=\frac{n!}{(n-m)!}\)

排列数

内容

\(n\)个物品中不放会地依次选\(m\)个物品,不考虑顺序,有多少种方案, 记作\(C_n^m\)

公式

\(C_n^m=\frac{A_n^m}{m!}=\frac{n!}{m!(n-m)!}\)

常用的计算方法

根据定义,显然可以通过预处理阶乘来算,

考虑\(C_n^{m-1}\)\(C_n^m\)的关系,显然有\(C_n^n=C_n^{m-1}*\frac{n-m+1}{m}\)

考虑将\(C\)展开,有

\(\begin{aligned}C_n^m&=\frac{n!}{m!(n-m)!}\\&=\frac{(n-1)!(m+n-m)}{m!(n-m)!}\\&=\frac{(n-1)!m+(n-1)!(n-m)}{m!(n-m)!}\\&=\frac{(n-1)!}{(m-1)!(n-m)!}+\frac{(n-1)!}{m!(n-m-1)!}\\&=C_{n-1}^{m-1}+C_{n-1}^{m}\end{aligned}\)

其实这就是杨辉三角

#### 性质

\(C_n^m=C_n^{n-m}\),根据组合的定义去证明

\(C_{n+m+1}^n=\sum_{i=0}^{m}C_{n+i}^i\)考虑杨辉三角的形式,\(C_n^m\)裂成\(C_{n-1}^{m-1}\)\(C_{n-1}^m\),同时我们又知道\(C_n^0=1\),所以最后一次裂了之后就直接\(C_n^0=C_{n-1}^0\)

\(C_n^mC_m^r=C_n^rC_{n-r}^{m-r}\),根据组合的定义去证明

\(\sum_{i=0}^nC_n^ix^i=(1+x)^n\),二项式定理

\(\sum_{i=0}^nC_n^i=2^n\),当\(x=1\)

\(\sum_{i=0}^nC_n^i(-1)^i=0\),当\(x=-1\)

\(C_n^0+C_n^2+\ldots=C_n^1+C_n^2+\ldots=2^{n-1}\),通过杨辉三角去裂开每一项

\(C_{n+m}^r=\sum_{i=0}^{min(n,m,r)}C_n^iC_m^{r-i}\),根据组合的定义去证明

\(C_{n+m}^n=C_{n+m}^m=\sum_{i=0}^{min(n,m)}C_n^iC_m^i\),通过上一个性质变形可得

\(mC_n^m=nC_{n-1}^{m-1}\),暴力展开即可

\(\sum_{i=0}^{n}iC_n^i=n2^{n-1}\),通过换元有\(\begin{aligned}\sum_{i=0}^{n}iC_n^i+\sum_{i=0}^{n}iC_n^{n-i}&=\sum_{i=0}^{n}iC_n^i+\sum_{i=0}^{n}(n-i)C_n^i\\&=\sum_{i=0}^{n}nC_n^i\\&=n\sum_{i=0}^{n}C_n^i\\&=n2^n\end{aligned}\)

由于\(\sum_{i=0}^niC_n^i=\sum_{i=0}^{n}(n-i)C_n^i\),所以\(\sum_{i=0}^{n}iC_n^i=n2^{n-1}\)

\(\sum_{i=0}^nC_n^ii^2=n(n+1)2^{n-2}\),考虑展开

\(\begin{aligned}\sum_{i=0}^nC_n^ii^2&=\sum_{i=0}^{n}\frac{n!}{i!(n-i)!}i(i-1+1)\\&=\sum_{i=0}^{n}i\frac{n!}{i!(n-i)!}+\sum_{i=0}^{n}i(i-1)\frac{n!}{i!(n-i)!}\\&=\sum_{i=0}^{n}C_n^ii+\sum_{i=1}^{n}i(i-1)\frac{n!}{i!(n-i)!}(当i=0的时候,那一项为0)\\&=n2^{n-1}+\sum_{i=0}^{n-1}(i+1)i\frac{n!}{(i+1)!(n-i-1)!}\\&=n2^{n-1}+\sum_{i=0}^{n-1}i\frac{n!}{i!(n-i-1)!}\\&=n2^{n-1}+n\sum_{i=0}^{n-1}i\frac{(n-1)!}{i!(n-i-1)!}\\&=n2^{n-1}+n\sum_{i=0}^{n-1}iC_{n-1}^i\\&=n2^{n-1}+n(n-1)2^{n-2}\\&=n(2*2^{n-2}+n2^{n-2}-2^{n-2})\\&=n(n+1)2^{n-2}\end{aligned}\)

\(\sum_{i=0}^{n}(C_n^i)^2=C_{2n}^n\),根据\(C_{n+m}^{n}=\sum_{i=0}^{min(n,m)}C_n^iC_m^i\),当\(n=m\)的时候成立

错排数

定义

\(n\)个编了号的球放进\(n\)个编了号的盒子里面满足每个盒子有且只有\(1\)个球,同时球的编号和盒子的编号不一样,满足这样的条件的方案总数

递推式

\(D(n)=(n-1)(D(n-1)+D(n-2)),D(1)=0,D(2)=1\)

假设已经有\(n-1\)个元素实现了错排,那么新来的元素不管是和谁交换位置都可以实现错排,此时的方案即为\((n-1)D(n-1)\)

如果已经有\(n-2\)个元素实现了错排,那么必然有一个元素刚好放在他编号一样的盒子里面,此时新来的元素必须和这个元素交换位置才能实现整体的错排,所以方案数即为\((n-1)D(n-2)\)

所以有\(D(n)=(n-1)(D(n-2)+D(n-1))\)

卡特兰数

定义

给定一个边长为\(n\)的正方形,考虑从左下角走到右上角,每一次只能往右走或者往上走,且不能越过对角线,即图中的红线,方案数记作卡特兰数\(C(n)\),即在任意时刻向右的步数大于等于向上的步数,

递推式

\(C(n)=\sum_{i=0}^{n-1}C(i)*C(n-i-1)\),考虑第一次触碰红线的时间即可

通项公式

\(C(n)=C_{2n}^n-C_{2n}^{n+1}=\frac{C_{2n}^n}{n+1}\)

如果不考虑红线的限制,那么方案数为\(C_{2n}^n\),因为操作序列长度为\(2n\),从中选\(n\)个操作是向上,剩下的\(n\)个是向右

接着考虑不合法的方案,如果即跨过了对角线,因为从上面跨到下面和下面跨到上面本质上是一样的,这里只讨论下面跨到上面的情况,我们如果把第一次接触到绿线以后的操作全部反转,即向右变成向上,向上变成向右,那么最终一定会到达\((n-1,n+1)\),因为第一次接触绿线的时候,设向右的步数为\(cnt\),那么向上的步数一定是\(cnt+1\), 剩下的向右的步数即为\(n-cnt\),向上的步数为\(n-cnt-1\),反转之后向右的步数为\(n-cnt-1\),向上的步数为\(n-cnt\),总过的向右的步数即为\(n-1\) ,向上的步数即为\(n+1\),所以最终一定会达到\((n-1,n+1)\),按照同样的分析方法可以得到此时的方案数为\(C_{2n}^{n+1}\)

后面那一步推导就是将两个组合数暴力展开再合并之后的结果

第一类stirling数

定义

考虑把\(n\)个不同的数字分成\(m\)个圆排列的方案数记作\(s_{n,m}\),圆排列指将循环后相等的序列看成同一个序列

递推式

\(s_{n,m}=s_{n-1,m-1}+(n-1)*s_{n-1,m}\)

边界条件即为\(s_{n,n}=1\)\(s_{n,1}=(n-1)!\)\(s[n][m]=0[n<m]\)\(s[n][0]=0[n>0]\)

递推式应该很好理解,就是第\(n\)个数是否是单独形成一个圆排列

第二类stirling数

定义

考虑把\(n\)个不同的数字分成\(m\)个集合的方案数记作\(S_{n,m}\)

递推式

\(S_{n,m}=S_{n-1,m-1}+m*S_{n-1,m}\)

与第一类stirling数类似

考虑第\(n\)个数是一个新的集合还是加入原有的集合

边界条件为\(\begin{cases}S_{n,n}=1\\S_{n,1}=1\\S_{n,m}=0(n<m)\\S_{n,0}=0(n>0)\end{cases}\)

性质

第二类stirling数和幂有一个奇妙的关系

\(n^k=\sum_{i=0}^{k}S(k,i)*C(n,i)*i!\)从组合意义上面去理解,这里的\(i\)为枚举非空盒子的数量


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM