1 条题解

  • 0
    @ 2025-8-24 22:02:56

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar WeLikeStudying
    

    搬运于2025-08-24 22:02:56,当前版本为作者最后更新于2021-08-20 23:52:03,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    素性测试

    • 测试一个数是否是质数。
    • 应该都学过 O(n)O(\sqrt n) 的试除法,基于 $\forall ab=c(a,b,c\in \mathbf{Z^+}),\min(a,b)\leq\sqrt c$
    • 米勒·拉宾算法,O(log2n)O(\log_2n) 的随机算法。(没错这就是正确率接近 100% 的随机算法)
    • 印度小哥有个非随机算法唤作 AKS,不过实用性稍低不讲,米勒·拉宾已经足够优秀同时足够简单
    • 米勒·拉宾算法基于费马小定理,即:
    pa,ap11(modp)\forall p\nmid a,\quad a^{p-1}\equiv1\pmod p
    • 于是便产生了一种朴素的想法:对于待测试的数 n ,随机一个 0<a<n0<a<n 计算 an1modna^{n-1}\bmod n 若结果不为 1 即可排除 n 是质数。
    • 但这显然是必要不充分条件,事实上,存在一些合数 n,满足:
    an,an11(modn)\forall a\bot n,\quad a^{n-1}\equiv1\pmod n
    • 这类数称为 Carmichael 数,如 561=3×11×17561=3×11×17 ,这类做法很难试出来,也许你见到的 Carmichael 数因子都很小,但是在题目的数据范围,我可以构造出 $n=999211956328352449=550177\times 1100353\times 1650529$,没错,他也是 Carmichael 数,此时,你又该如何应对!
    • 哈哈哈,是不是没有办法了。
    • 所以还需要平方差公式奇素数判定,除了 2 以外的质数,都是奇数!而奇数减一,就是偶数啊!两句大废话。
    • 换句话说,若 an11(modn)a^{n-1}\equiv1\pmod n 则 $(a^{\frac{n-1} 2}+1)(a^{\frac{n-1} 2}-1)\equiv0\pmod n$,也就是 an121(modn)a^{\frac{n-1} 2}\equiv-1\pmod nan121(modn)a^{\frac{n-1} 2}\equiv1\pmod n ,如果 n12\frac {n-1} 2 是偶数,还可以验证 n14\frac {n-1} 4
    • 也就是说我们实际上用来判定的定理是:令 n1=2xyn-1=2^x\cdot y (y 是奇数),若 nPn\in\mathbf{P} 则 ,对于 ay1(modn)a^y\equiv1\pmod n 或 $\exists k\in[0,x],\quad a^{2^k\cdot y}\equiv-1\pmod n$(其实那个区间是左闭右开的,我只是讨厌下划线,而且加上也不影响)
    • 用原根我们不会涉及的原理可以证明至少存在一个 ana\bot nnn 是合数的情况判掉。
    • 仍然不会涉及的知识可以证明对于奇合数 nn 非证据的数目至多是 n14\dfrac{n-1}4
    • 根据维基百科的说法,用不超过 37 的质数即可判定 2642^{64}1844674407370955161618446744073709551616 范围的 n 。
    • 代码实现
    • 如果不是枚举再快速幂是可以做到 O(log2n)O(\log_2n) 而非 O(log22n)O(\log_2^2n) 的。
    • 注意 OI wiki\text{OI wiki} 上给出的复杂度是考虑了巨大整数无法进行 O(1)O(1) 加减乘除的情况。

    质因数分解

    • 试除法仍然是 O(n)O(\sqrt n) 复杂度。
    • Pollard-rho 可以做到理论 O(n4log2n)O(\sqrt[4]n\log_2n) 的复杂度,且在实际运行中跑得飞快。
    • 主要思想是如何快速用随机数试出某个数的因子。
    • 生日攻击悖论:对于一个理想的取值为 [1,n][1,n] 的随机数生成器,生成 πn2\sqrt {\dfrac{\pi n}2} 个数期望得到两个数相同。
    • 如果一个数 nn 不是质数,那么它的最小质因子 pxp_x 满足 pnp\leq\sqrt n
    • 如果我们随机生成 n4\sqrt[4]n 个数,那么期望存在两个数 a,ba,b 使得 ab(modpx)a\equiv b\pmod {p_x}。那么 ab|a-b| 就是 p 的倍数,可以通过求 gcd(ab,n)\gcd(|a-b|,n) 得到一个因子。
    • 但我们发现这样做复杂度又乘了回去,由于还有 gcd 的运算,复杂度直接变成 O(nlog2n)O(\sqrt n\log_2n)
    • Pollard 构造了这样一组数列 {ai}\lbrace a_i\rbrace 满足 a0=xa_0=xai+1=(ai2+c)modna_{i+1}=(a_i^2+c)\bmod n
    • 这个数列有一个不得了的性质,任取数列中的两个数 ai,aja_i,a_j ,若 aiaj0(modpx)a_i-a_j\equiv0\pmod {p_x}根据平方差公式:
    ai+1aj+1=(ai2+c)(aj2+c)=ai2aj2a_{i+1}-a_{j+1}=(a_i^2+c)-(a_j^2+c)=a_i^2-a_j^2 =(ai+aj)(aiaj)0(modpx)=(a_i+a_j)(a_i-a_j)\equiv0\pmod {p_x}
    • 所以我们最好每次检查不同的距离。
    • 这样的数据随机性当然有保证。
    • 给张 Excel 自制散点图看一下。(a0=3222,ai+1=(ai2+24)mod9409a_0=3222,a_{i+1}=(a_i^2+24)\bmod9409,给出数列的前 1000 项) 你能看出这张图是循环的吗
    • 大概一看,的确是分布地比较均匀,不过仔细一看你就会发现:数列之间好像有循环?
    • 没错,显然这种伪随机数一旦出现相同的就会循环,而由于数据范围固定,很容易进入循环,形成混循环(即rho形结构)(我之前的数据经过特制,使得环长较长,实际环长还要更短)(如图 a0=121,ai+1=(ai2+11)mod1014a_0=121,a_{i+1}=(a_i^2+11)\bmod 1014),如果进入循环还一直不停地检查就太傻了。
    • 给出一种名叫 floyd (没错就是弗洛伊德)的判环方法。
    • kk 次操作让一个数为 aka_k ,另一个数为 a2ka_{2k}(这很容易做到,迭代两次就好了),如果 ak=a2ka_k=a_{2k} 那么显然已经进入环中,直接跳出即可。
    • 显然进入环中后 kk 会慢慢增加到某个环长的倍数,终止可行。
    • 而且这样每次只要检查 gcd(a2kak,n)\gcd(|a_{2k}-a_k|,n) 就能够保证不同的距离,可谓一举两得。(另外 a2k=aka_{2k}=a_k 时,即使整除)
    • 不过,你有没有发现一些问题?
    • 这其实并没有做到检查全部的数对,你可以暂时理解成,由于以上的性质,其实我们已经检查了大部分的情况。毕竟我们绝对不能讲超过高中的知识
    • 刚才那个问题令我们想到一个问题,就是如果环长为1,那么一下子慢的跳到 a1a_1,快的跳到 a2a_2,下一步直接结束然后无限循环,所以正确的做法是在第 kk 步跳到 ak1a_{k-1}a2k1a_{2k-1}
    • 如果我们给的伪随机数随机性够高,且我们的检测方法事实上接近于两两求差,则期望在 O(p4log2n)O(\sqrt[4] p\log_2n) 的复杂度内给出 nn 的最小质因子 pp
    • 很遗憾,这里不能给出复杂度的严格证明,但事实上时间复杂度并没有因为多次寻找因子而改变,仍然是 O(n4log2n)O(\sqrt[4]n\log_2n),给出一个表格看一下这个算法在不同数字下的运算次数当然在我的程序下
    数字 质因数分解式 最大运算次数 最小运算次数 平均运算次数
    92233720368547758089223372036854775808 2632^{63} 36113611 21582158 2900.722900.72
    92233720368547757839223372036854775783 是质数 610610
    92233719944822430499223371994482243049 303700049323037000493^2 64344446434444 245074245074 2301568.322301568.32
    92232532901085832079223253290108583207 209714332097143^3 239219239219 2860928609 111099.06111099.06
    21474836482147483648 2312^{31} 12461246 843843 1004.331004.33
    21474836472147483647 是质数 300300
    21471175692147117569 46337246337^2 1425514255 487487 5971.985971.98
    21417005692141700569 128931289^3 36553655 522522 1890.791890.79
    • 代码实现
    • 上例题!
    • 【模板】Pollard-Rho算法
    • 这道题靠之前那个模板会 TLE 一个点。
    • 于是我们可以进行一个简单的优化,我们可以尝试把一段数乘起来 modn\bmod n 来减少常数。
    • 但这样可能会导致 floyd 过晚退出,而且乘出 nn (一点用也没有)的概率也会增加。
    • 我们可以进行一个折中的方法:每隔约 log(n)\log(n) 次进行一次 gcd,然后就可以消掉 gcd 的复杂度,理论时间复杂度为 O(n4)O(\sqrt[4]n)
    • AC 地太轻松了?
    • 再次给出这个算法复杂度的表。
    数字 质因数分解式 最大运算次数 最小运算次数 平均运算次数
    92233720368547758089223372036854775808 2632^{63} 96259625 14441444 3624.043624.04
    92233720368547757839223372036854775783 是质数 610610
    92233719944822430499223371994482243049 303700049323037000493^2 805484805484 38033803 249223.63249223.63
    92232532901085832079223253290108583207 209714332097143^3 2793127931 699699 7487.537487.53
    21474836482147483648 2312^{31} 15781578 7474 265.49265.49
    21474836472147483647 是质数 300300
    21471175692147117569 46337246337^2 54465446 345345 1407.821407.82
    21417005692141700569 128931289^3 29182918 126126 687.6687.6
    • 对比即可发现这个简单优化的优越性能。
    • 不过 Pollard 算法确实不是很稳定。
    • 代码实现
    • 1

    信息

    ID
    3696
    时间
    4000ms
    内存
    32MiB
    难度
    6
    标签
    递交数
    0
    已通过
    0
    上传者