数论总结(下)(高级数论)

一.\(BSGS\)算法(大步小步算法)

求解:$$a^x\equiv b\pmod{c}$$
其中\(c\)为质数
我们把\(x\)拆为\(x = im - j\)
那么:\(a^x\equiv b\pmod{c}\) ------->\(a^{im-j}\equiv b\pmod{c}\)
\(a^{im-j}\equiv b\pmod{c}\) --------> \(ba^j\equiv a^{im}\pmod{c}\)
显然\(m\)\(\sqrt{c}\)最舒服,那么令\(m=\sqrt{c}\)
然后枚举\(i\)\(i \leq \sqrt{c}\)),把\(a^{im}\)\(map\)或者\(hash\)存起来。
这样我们再枚举\(j\),只需要检查\(ba^j\)是否已经存在即可,
假如存在,那么此时\(x=im-j\)即为方程的解。
代码参考:戳我!

二.卢卡斯定理(Lucas)

1.卢卡斯定理:

就一句话:

\[C_n^m \% p = C_{n\% p}^{m\%p}*C_{n/p}^{m/p} \]

其中\(p\)为质数,一般用于计算比较大的组合数。

2.扩展Lucas定理:

\(p\)不为质数的时候,先唯一分解:

\[p = p_1^{k_1}*p_2^{k_2}....P_n^{k_n} \]

那么其实就是求解下面这个方程组中的\(ans\)
\(ans\equiv c_1\pmod{p_1^{k_1}}\)
\(ans\equiv c_2\pmod{p_2^{k_2}}\)
\(......\)
\(ans\equiv c_n\pmod{p_n^{k_n}}\)
其中\(c_1\)\(c_2\)\(...\)\(c_n\)为对应\(C_n^m \% p_t^{k_t}\)的答案。
假设我们可以求得\(c_1\)\(c_2\)\(...\)\(c_n\),那么直接\(CRT\)一波即可得到\(ans\)
所以现在问题变为求解\(C_n^m\%p^k\)。 我们知道:\(C_n^m = \frac{n!}{m!(n-m)!}\)
所以其实我们就是要求\(n! \% p^k\)。这个东西这么求呢?
首先,我们等会是要求逆元的,所以应该要先把\(p\)提出来,等会算完逆元后再乘回去即可。
所以我们可以把上面的\(n!\)拆为:

\[n! = p^t*\lfloor \frac{n}{p} \rfloor !*G(n) \]

其中\(G(n)\)为不是\(p\)倍数的数的乘积,显然\(\lfloor \frac{n}{p} \rfloor !\)可以递归求解。
\(p^t\)是提出来的最后再一起算,所以我们只要考虑如何算\(G(n)\)
观察到\(G(n) \% p^k\)是以\(p^k\)为周期的,所以肯定可以数论分块,分块搞一下即可。
最后考虑一下\(p^t\)这一项,一个\(\prod_{i=1}^{n}\)中约数\(p\)的个数为\(\sum_{r=1}^{p^r \leq n} {\lfloor \frac{n}{p^r}} \rfloor\),可以直接算出\(t\)
然后就做完了,记得最后强行\(CRT\)一波合并\(c_i\)即可。
这里给出求解\(C_n^m\%p^k\)部分的代码:

注:代码中的pk代表p^k,是事先求好了的。
LL fac(LL n,LL p,LL pk)            //计算(n!) % (p^k)
{
    if(n==0)return 1;
    LL res=1;
    for(LL i=2;i<=pk;i++)                 //求解G(n)中的整块:
        if(i%p)res=(res*i)%pk;                   
    res=pow(res,n/pk,pk);                //res = res^(n/(p^k)) //分块一波
    for(LL i=2;i<=n%pk;i++)                     
        if(i%p)res=(res*i)%pk;            //求解G(n)中的余项。
    return (res*fac(n/p,p,pk))%pk;        //递归求解。
}
LL Calc(LL n,LL m,LL p,LL pk)
{
    if(n<m)return 0;
    LL a=fac(n,p,pk),b=fac(m,p,pk),c=fac(n-m,p,pk);
    //得到了a、b、c中的G(x)与floor(n/p)!, 因为要提出p先不算所以不求解p^t
    LL k=0;
    for(LL i=n;i;i/=p)k+=i/p;                        
    for(LL i=m;i;i/=p)k-=i/p;
    for(LL i=n-m;i;i/=p)k-=i/p;            //求解C(n,m)中质因子p的个数(t)。
    LL res=(((a*inv(b,pk))%pk)*inv(c,pk))%pk * pow(p,k,pk)%pk;
    //把质因子p(p^t)乘回来
    return res;
}

三.提取公因数

这个其实不能算是数论吧.... 但是这里列一个专题是因为它太重要了
提取公因数数论分块被称为数论两大套路(笔者自己yy不要管啦)
提取公因式衍生出来的是“考虑每一项对答案的贡献”,一般涉及这种思想的题目都是非常难的。
其实具体还是要见题拆题。这里给两个例子:

例子1

假如\(a^2-ab = E^2\) , 那么如何快速求解符合条件的\((a、b)\)二元组?
\(a^2-ab = a(a-b) = E^2\),所以\(a=F^2\) , \(a-b=S^2\)
所以\(F^2-b = S^2\),即得到:\(b = (F-S)(F+S)\)
枚举量一下子就大幅度的减少了。

例子2

求解\(\sum_{i=1}^n \sum_{j=1}^m gcd(i,j)\)
\(d=gcd(i,j)\)给提出来。

\[\sum_{i=1}^n \sum_{j=1}^m gcd(i,j) = \sum_{d=1}^{min(n,m)}d\sum_{i=1}^n \sum_{j=1}^m [gcd(i,j)=d] = \sum_{d=1}^{min(n,m)}d\sum_{i=1}^{ \lfloor \frac{n}{d} \rfloor } \sum_{j=1}^{\lfloor \frac{m}{d} \rfloor } [gcd(i,j)=1] \]

然后至于\(\sum_{i=1}^{ \lfloor \frac{n}{d} \rfloor } \sum_{j=1}^{\lfloor \frac{m}{d} \rfloor } [gcd(i,j)=1]\)怎么快速求见后面的莫比乌斯反演。

例子3

求解\(\sum_{d=1}^n \phi(d) \sum^{\lfloor \frac{n}{d} \rfloor}_{i=1} {\lfloor \frac{n}{id} \rfloor}\),多组数据。
\(T = id\) , 然后把\(T\)给提出来。

\[\sum_{d=1}^n \phi(d) \sum^{\lfloor \frac{n}{d} \rfloor}_{i=1} {\lfloor \frac{n}{id} \rfloor} = \sum_{T = 1}^n{\lfloor \frac{n}{T} \rfloor} \sum_{d|T} \phi(d) \]

后面的\(\sum_{d|T} \phi(d)\)可以线性筛,具体见后面相关内容,从而使得询问时单组复杂度由\(O(n)\)变为了\(O(\sqrt{n})\)

四.莫比乌斯反演(\(Mobius\))

当一个函数\(F(n)\)很好求,而其可以写成其约数或倍数\(d\)的函数\(f(d)\)\(f(d)\)很不好求时,
我们可以通过莫比乌斯反演用\(F(n)\)反推\(f(d)\),从而实现快速求解\(f(d)\)
其实就是容斥原理的体现,证明见这里 戳我!
莫比乌斯反演有两种形式:

1.约数形式:

若有\(F(n) = \sum_{d|n} f(d)\) 那么$$f(n) = \sum_{d|n} \mu(\frac{n}{d})*F(d)$$

2.倍数形式:

若有\(F(n) = \sum_{n|d} f(d)\) 那么$$f(n) = \sum_{n|d} \mu(\frac{d}{n})*F(d)$$

3.莫比乌斯函数:

莫比乌斯函数即为\(\mu(d)\),定义如下:
(1)若\(d=1\),那么\(\mu(d)=1\)
(2)若\(d=p_1p_2p_3...p_k\)\(p_1,p_2...\)均为互异素数,那么\(\mu(d)=(-1)^k\)
(3)其它情况下\(\mu(d)=0\)
莫比乌斯函数为积性函数,可以线性筛出,下面给出代码:

void Mobius(){
    memset(Isprime,true,sizeof(Isprime));
    mu[1] = 1; Isprime[1] = false;
    for(int i = 2; i <= n; i ++){
        if(Isprime[i]){prm[++cnt] = i; mu[i] = -1;}
        for(int j = 1; prm[j]*i<=n && j <= cnt; j ++){
            Isprime[i*prm[j]] = false;
            if(i%prm[j] == 0){mu[i*prm[j]] = 0; break;}
            else mu[i*prm[j]] = -mu[i];
        }
    }
}

4.莫比乌斯反演与\(\sum [gcd(i,j)=1]\)

求解\(gcd(i,j)\)相关问题是莫比乌斯反演最常见的应用。
当遇到涉及莫比乌斯反演的题目时,一般也是先把其转化为求\(\sum_{i=1}^n \sum_{j=1}^m [gcd(i,j)==1]\)
下面给出求解\(\sum_{i=1}^n \sum_{j=1}^m [gcd(i,j)==1]\)\((n\leq m)\)的莫比乌斯反演过程:
\(f(d) = \sum_{i=1}^n \sum_{j=1}^m [gcd(i,j)==d]\)
然后令:

\[F(n) = f(d)+f(2d)+...+f(kd)=\sum_{n|d} f(d) \]

所以\(F(d)\)即表示\(gcd(i,j)\)\(d\)的倍数的二元组的个数。
那么显然\(F(d)\)非常好求:

\[F(d) = \lfloor \frac{n}{d} \rfloor * \lfloor \frac{m}{d} \rfloor \]

又因为\(F(n) = \sum_{n|d} f(d)\)
所以通过莫比乌斯反演得到:

\[f(n) = \sum_{n|d}^{d \leq n} \mu(\frac{d}{n})F(d) \]

所以:

\[f(1) = \sum_{i=1}^{n} \mu(i)F(i) = \sum_{i=1}^{n} \mu(i)\lfloor \frac{n}{i} \rfloor * \lfloor \frac{m}{i} \rfloor \]

先求一下\(\mu(i)\)的前缀和,然后数论分块就行了。
上面得到的关系式非常重要,你甚至可以把它记下来(n<=m):

\[\sum_{i=1}^n \sum_{j=1}^m [gcd(i,j)=1]=\sum_{i=1}^{n} \mu(i)\lfloor \frac{n}{i} \rfloor * \lfloor \frac{m}{i} \rfloor \]

5.莫比乌斯反演习题:

见莫比乌斯反演题目列表:[戳这里!] (http://www.cnblogs.com/GuessYCB/p/8277359.html)

五.积性函数线性筛 与 狄利克雷卷积

1.积性函数 与 线性筛

a.积性函数相关定义

(1)积性函数:
若存在函数\(f(x)\) ,当\((x,y)=1\)时 , 满足\(f(x*y)=f(x)*f(y)\),则称\(f(x)\)为积性函数。
(2)完全积性函数:
若对于任意\(x,y\),有\(f(x*y) = f(x)*f(y)\),则称\(f(x)\)为完全积性函数。
(3)常见的积性函数:
莫比乌斯函数\(\mu(i)\) 、 欧拉函数\(\phi(i)\)

b.积性函数的线性筛(sieve)

完全积性函数的筛法就不讲了,直接乘起来即可。
那么对于积性函数\(f(x)\)的线性筛,我们要求下面几个东西有定义或很好求:
(1)\(f(1)\)
(2)\(f(p)\) , (\(p\)为质数)
(3)\(f(p^k)\) , (\(p\)为质数)
其中\(f(1) , f(p)\)一般为\(O(1)\)算,\(f(p^k)\)则一般从\(f(p^{k-1})\)推来。
那么假设我们已经求解的上面函数的取值,考虑如何筛。
由积性函数的定义我们可以知道\(f(x*y)=f(x)*f(y)\)的前提条件为\((x,y)=1\)
所以当我们要筛一个\(f(n)\)时,我们要把\(n\)拆为两个互质的数\(x,y\)的乘积。
显然最优秀的办法就是让\(x\)取最小值因子,把最小质因子\(x^k\)分离出来,这样就可以套上线性筛了。
我们记\(low(i)\)表示\(i\)的最小质因子\(x\)\(x^k\)
那么根据线性筛的原则,我们分两种情况讨论(\(i,prm[j]\)含义见素数线性筛):
(1)当\(i\%prm[j] != 0\)
这太容易了不是吗,\(i\)\(prm[j]\)互质,直接套定义即可。

\[f(i*prm[j]) = f(i)*f(prm[j]);low(i*prm[j]) = prm[j] \]

(2)当\(i\%prm[j]==0\)
首先\(low(i*prm[j]) = low(i)*prm[j]\)这没有什么好说的吧。
然后:
第一种情况:\(low(i)=i\),那么\(i\)\(p^k\)形式,用特殊方法计算。
第二种情况:\(low(i)!=i\),那么我们把\(prm[j]*i\)拆为两个互素的数,具体为把\(prm[j]\)给除干净:

\[f(i*prm[j] = f(i\ /\ low[i]) * f(low[i]*prm[j]) \]

然后就没有了。 下面给出具体实现代码:

void sieve(){
    low[1] = 1; f[1] = 对f(1)的特殊定义; tot = 0;
    for(int x = 2; x <= n; x ++){
        if(!Prime[x]){prm[++tot] = x; f[x] = 对f(p)的特殊定义; low[x] = x;}
        for(int j = 1; j <= tot && x*prm[j] <= n; j ++){
            Prime[x*prm[j]] = true;
            if(x%prm[j] == 0){
                low[x*prm[j]] = low[x] * prm[j];
                if(x == low[x])
                    f[x*prm[j]] = 对f(p^k)的特殊定义;
                else
                    f[x*prm[j]] = f[x/low[x]] * f[prm[j]*low[x]];
                break;
            }
            low[x*prm[j]] = prm[j];
            f[x*prm[j]] = f[x] * f[prm[j]] % mod;
        }
    }
    return;
}

2.狄利克雷卷积

其实懂了积性函数的线性筛之后这玩意就是来搞笑的。
对于积性函数\(f(x)\)\(g(x)\)它们的狄利克雷卷积也是积性函数
其中它们的狄利克雷卷积长这样:

\[(f*g)(n) = \sum_{d|n} f(d) g(\frac{n}{d}) \]

狄利克雷卷积的性质:
交换律:\((f*g) = (g*f)\)
结合律:\((f*g)*h = f*(g*h)\)
狄利克雷卷积自身不难,把它当做积性函数一样的线性筛即可。

六.杜教筛

....还不会,到时候回来补(先去搞点其他的换脑子啦):
留下学习资料:
http://blog.csdn.net/skywalkert/article/details/50500009
http://www.cnblogs.com/zhoushuyu/p/8301660.html

posted @ 2018-01-11 14:57  GuessYCB  阅读(1190)  评论(0编辑  收藏  举报