『正睿OI 2019SC Day7』

<更新提示>

<第一次更新>


<正文>

简单数论

质因子分解

素性测试

素性测试指的是对一个正整数是否为质数的判定,一般来说,素性测试有两种算法:

\(1.\) 试除法,直接尝试枚举因子,时间复杂度\(O(\sqrt n)\)

\(2.\) \(Miller-Rabin\)算法,利用费马小定理和二次探测定理对素数进行测试,有小概率误判,时间复杂度\(O(log_2n)\)

\(Code:\)

inline bool judge(long long x,long long p)
{
    if ( x % p == 0 || quickpow( p%x , x-1 , x ) != 1 ) return false;
    register long long k = x-1 , val;
    while ( ( k & 1 ) == 0 )
    {
        val = quickpow( p%x , k>>=1 , x );
        if ( val != 1 && val != x-1 ) return false;
        if ( val == x-1 ) return true;
    }
    return true;
}
inline bool MillerRabin(long long x)
{
    if ( x == 2 ) return true;
    if ( x < 2 || ( x & 1 ) == 0 ) return false;
    for (int i=0;i<10;i++)
    {
        if ( x == P[i] ) return true;
        if ( !judge( x , P[i] ) ) return false;
    }
    return true;
}

质因子分解

质因子分解则指的是对一个正整数按照算术基本定理的形式进行分解,也有两种常用算法:

\(1.\) 试除法,直接尝试枚举因子,时间复杂度\(O(\sqrt n)\)

\(2.\) \(Pollard's\ Rho\)算法,配合\(Miller-Rabin\)算法对整数进行随机化分解,期望时间复杂度\(O(\sqrt [4]{n}log_2n)\)

\(Code:\)

inline long long Euclid(long long a,long long b) { return b == 0 ? a : Euclid(b,a%b); }
inline long long Random(void) { return ( 0LL + rand() ) << 30 | rand(); }
inline void PollardsRho(long long n)
{
    if ( n == 1 ) return;
    if ( MillerRabin( n ) ) { ans = max( ans , n ); return; }
    long long x = Random() % n , c = Random() % n , y = x;
    for (int i=2,k=2;true;i++)
    {
        register long long val = Euclid( n + y - x , n );
        if ( val > 1 && val < n )
            return PollardsRho(val) , PollardsRho(n/val);
        if ( i == k ) k <<= 1 , y = x;
        x = ( quickmul( x , x , n ) + c ) % n;
        if ( x == y ) break;
    }
    return PollardsRho( n );
}

数论算法

欧几里得算法

利用辗转相除法求两个数的最大公约数,时间复杂度\(O(log_2n)\)

\(Code:\)

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

扩展欧几里得算法

利用欧几里得算法求不定方程的特解,时间复杂度\(O(log_2n)\)

\(Code:\)

inline int Extended_Euclid(int a,int &x,int b,int &y,int c) 
{ 
    if ( b == 0 ) { x = c/a , y = 0; return a; } 
    else 
    { 
        int p = Extended_Euclid(b,x,a%b,y,c); 
        int x_ = x , y_ = y; 
        x = y_ , y = x_ - a / b * y_; 
        return p; 
    } 
}

类欧几里得算法

利用整除拆分技巧计算整除求和式,时间复杂度\(O(log_2n)\)

\(Code:\)

inline long long f(long long a,long long b,long long c,long long n)
{
    if ( a == 0 ) return ( n + 1 ) * ( b / c ) % Mod;
    if ( a >= c || b >= c )
        return ( f( a%c , b%c , c , n ) + n * ( n + 1 ) % Mod * INV2 % Mod * ( a / c ) % Mod + ( n + 1 ) * ( b / c ) % Mod ) % Mod;
    long long val = ( a * n + b ) / c;
    return ( ( n * val % Mod - f( c , c - b - 1 , a , val-1 ) ) % Mod + Mod ) % Mod;
}

中国剩余定理

利用公式计算线性同余方程组的特解,要求模数互质,时间复杂度\(O(nlog_2n)\)

\(Code:\)

inline void CRT(void)
{
    m_ = 1;
    for (int i=1;i<=n;i++)
        m_ *= m[i];
    for (int i=1;i<=n;i++)
        M[i] = m_ / m[i];
    for (int i=1;i<=n;i++)
    {
        long long y;
        Exeuclid(M[i],t[i],m[i],y,1);
        ans += a[i]%m_ * M[i]%m_ * t[i]%m_;
        ans %= m_;
    }
}

拓展中国剩余定理

利用扩展欧几里得算法每次求一个同余方程的解,然后依次合并,从而求得线性同余方程组的解,不要求模数互质,时间复杂度\(O(nlog_2n)\)

\(Code:\)

inline long long ExCRT(void)
{
    long long m_ = m[1] , x = r[1];
    for (int i=2;i<=n;i++)
    {
        long long x_,y_;
        if ( ( r[i] - x ) % Euclid( m_ , m[i] ) ) return -1;
        long long p = Exeuclid( m_ , x_ , m[i] , y_ , r[i] - x );
        long long Mod = m[i] / p;
        //要对当前的解先取模,防止爆longlong
        x_ = ( x_ % Mod + Mod ) % Mod;
        x += x_ * m_;
        m_ = m_ * m[i] / p;
        x = ( x + m_ ) % m_;
    }
    return x;
}

BSGS算法

利用分块思想求解离散对数问题的解,要求模数与底数互质,时间复杂度\(O(\sqrt n)\)

\(Code:\)

inline long long Baby_Step_Giant_Step(long long a,long long b,long long p)
{
    map < long long , long long > hash; hash.clear();
    long long t = (long long)sqrt(p) + 1 , mul = 1;
    b %= p; 
    for (int j=0;j<t;j++)
    {
        hash[ mul * b % p ] = j;
        mul = mul * a % p;
    }
    if ( a % p == 0 ) return b == 0 ? 1 : -1;
    a = 1;
    for (int i=0;i<=t;i++)
    {
        long long j = ( hash.find(a) != hash.end() ? hash[a] : -1 );
        if ( j >= 0 && i * t - j >= 0 ) return i * t - j;
        a = a * mul % p;
    }
    return -1;
}

ExBSGS算法

利用同余性质缩小模数规模,直至模数与底数互质,使用\(BSGS\)算法求解离散对数,时间复杂度\(O(\sqrt n)\)

\(Code:\)

inline long long ExBSGS(long long a,long long b,long long p)
{
    if ( b == 1 ) return 0;
    long long cnt = 0 , d , k = 1;
    while ( ( d = Euclid(a,p) ) ^ 1 )
    {
        if ( b % d ) return -1;
        b /= d , p /= d , ++cnt;
        k = k * ( a / d ) % p;
        if ( k == b ) return cnt;
    }
    unordered_map < long long , long long > Hash; Hash.clear();
    long long t = (long long)sqrt(p) + 1 , mul = 1;
    for (int j=0;j<t;j++)
    {
        Hash[ mul * b % p ] = j;
        mul = mul * a % p;
    }
    for (int i=0;i<=t;i++)
    {
        long long j = ( Hash.find(k) != Hash.end() ? Hash[k] : -1 );
        if ( j >= 0 && i * t - j + cnt >= 0 ) return i * t - j + cnt;
        k = k * mul % p;
    }
    return -1;
}

原根

分解模数可以判定原根的存在性,同时可以根据欧拉定理求解原根。

\(Code:\)

pending further supplement

二次剩余

如果存在原根,则可以利用\(BSGS\)算法计算二次剩余,时间复杂度\(O(\sqrt n)\)

\(Code:\)

pending further supplement

\(Tonelli–Shanks\)算法可以在\(O(log^2n)\)的时间内计算二次剩余。

\(Code:\)

pending further supplement

\(Cipolla\)算法可以在\(O(log_2n)\)的时间内计算二次剩余。

\(Code:\)

pending further supplement

数论函数

定义和概念

数论函数:定义域为正整数集,陪域为复数域的函数。

积性函数:对于任意\(a,b\in N^+,gcd(a,b)=1\),有\(f(ab)=f(a)f(b)\)的数论函数\(f\)被称为积性函数。

完全积性函数:对于任意\(a,b\in N^+\),有\(f(ab)=f(a)f(b)\)的数论函数\(f\)被称为完全积性函数。

积性函数的性质

\(1.\)\(n=\prod p_i^{a_i}\),则有\(f(n)=\prod f(p_i^{a_i})\)

\(2.\) 若数论函数\(f,g\)均为积性函数,则\(f*g,f/g\)也均为积性函数。

狄利克雷卷积

对于数论函数\(f,g\),我们定义\(f\)\(g\)\(dirichlet\)卷积为:

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

狄利克雷卷积的性质

\(1.\) 交换律:\(f\times g=g\times f\)

\(2.\) 结合律:\((f\times g)\times h=f\times (g\times h)\)

\(3.\) 分配律:\(f\times (g+h)=f\times g+ f\times h\)

\(4.\) 单位元:\(f\times \epsilon=f\)

\(5.\) 积性性:若函数\(f,g\)是积性函数,则\(f\times g\)也是积性函数。

莫比乌斯反演

莫比乌斯定理:若\(g=f\times I\),则\(f=g\times \mu\)

证明:

\[g=I\times f\\ \mu\times g =\mu \times I \times f\\\mu \times g=\epsilon \times f=f \]

底和顶

\(1.\) \(x\geq n⇔\lfloor x\rfloor\geq n\)

\(2.\) \(x> n⇔\lceil x\rceil> n\)

\(3.\) \(x\leq n⇔\lceil x\rceil\leq n\)

\(4.\) \(x< n⇔\lfloor x\rfloor< n\)

\(5.\) 对于\(i\in[1,n]\)\(\lfloor \frac{n}{i} \rfloor,\lceil \frac{n}{i} \rceil\)都只有\(O(\sqrt n)\)种不同的取值。

杜教筛

对于计算数论函数\(f\)的前缀和,如果我们能够找到合适的函数\(g\),并能快速计算函数\(g\)和函数\(f\times g\)的前缀和,那么我们就可以快速计算函数\(f\)的前缀和。

公式:

\[\sum_{i=1}^nf(i)=\sum_{i=1}^n(f\times g)(i)-\sum_{i=2}^ng(i)\sum_{j=1}^{\lfloor \frac{n}{i} \rfloor}f(j) \]

另一种形式,设\(S(n)=\sum_{i=1}^nf(i)\),那么有:

\[S(n)=\sum_{i=1}^n(f\times g)(i)-\sum_{i=2}^ng(i)S \left (\lfloor \frac{n}{i} \rfloor \right ) \]

可以用整除分块和记忆化搜索来计算,当我们线性筛预处理前\(n^{\frac{2}{3}}\)个前缀和时,杜教筛的时间复杂度为\(O(n^{\frac{2}{3}})\)

\(Code:\)

inline pll BishopSieve(int n)
{
    if ( n <= m ) return make_pair( phi[n] , mu[n] );
    if ( sum.count( n ) ) return sum[n];
    long long res1 = 1LL * n * ( n + 1 ) / 2 , res2 = 1;
    for (int l=2,r;l<=n;l=r+1)
    {
        r = n/l ?  min( n/(n/l) , n ) : n;
        pll val = BishopSieve( n/l );
        res1 -= 1LL * ( r - l + 1 ) * val.first;
        res2 -= 1LL * ( r - l + 1 ) * val.second;
    }
    return sum[n] = make_pair( res1 , res2 );
    // 求欧拉函数和莫比乌斯函数的前缀和
}

总结

感觉其实数论算法还有不少没有学,并且最大的问题就是做的题不够多,很多经典的套路还不够熟练,尤其是数论函数这一块。再就是代码不够熟练,还需要多刷题。


<后记>

posted @ 2019-08-16 14:31  Parsnip  阅读(265)  评论(0编辑  收藏  举报