Loading [MathJax]/jax/element/mml/optable/GeneralPunctuation.js

『正睿OI 2019SC Day7』

Parsnip·2019-08-16 14:31·267 次阅读

『正睿OI 2019SC Day7』

<更新提示>

<第一次更新>


<正文>

简单数论#

质因子分解#

素性测试#

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

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

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

Code:

Copy
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(n)

2. Pollards Rho算法,配合MillerRabin算法对整数进行随机化分解,期望时间复杂度O(4nlog2n)

Code:

Copy
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(log2n)

Code:

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

扩展欧几里得算法#

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

Code:

Copy
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(log2n)

Code:

Copy
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(nlog2n)

Code:

Copy
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(nlog2n)

Code:

Copy
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(n)

Code:

Copy
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(n)

Code:

Copy
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:

Copy
pending further supplement

二次剩余#

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

Code:

Copy
pending further supplement

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

Code:

Copy
pending further supplement

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

Code:

Copy
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,我们定义fgdirichlet卷积为:

(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:

Copy
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 @   Parsnip  阅读(267)  评论(0编辑  收藏  举报
编辑推荐:
· 理解Rust引用及其生命周期标识(下)
· 从二进制到误差:逐行拆解C语言浮点运算中的4008175468544之谜
· .NET制作智能桌面机器人:结合BotSharp智能体框架开发语音交互
· 软件产品开发中常见的10个问题及处理方法
· .NET 原生驾驭 AI 新基建实战系列:向量数据库的应用与畅想
阅读排行:
· 2025成都.NET开发者Connect圆满结束
· 后端思维之高并发处理方案
· 千万级大表的优化技巧
· 在 VS Code 中,一键安装 MCP Server!
· 10年+ .NET Coder 心语 ── 继承的思维:从思维模式到架构设计的深度解析
点击右上角即可分享
微信分享提示
目录