Cry_For_theMoon  

做 zr 模拟赛的时候有这样一道题目:

给出 n 个数 a1,a2,...,an。求他们的 lcm mod998244353 的结果。

n5000,ai1018

用 pollard-rho 的人希望你的考场上也能写出来这个东西,那我就没意见了,我先投降。

考虑最朴素的求 n 个数的 lcm 的过程:假设我们已经算出了 x=lcm(a1,a2,...,ai),我们现在想求 xai+1gcd,然后把 x 乘上 ai+1gcd(x,ai+1) 就是前 i+1 个数的 lcm。

难点就是求:gcd(ai+1,lcm(a1,a2,...,ai))

第一个想法:这个式子显然可以化成:lcm(gcd(ai+1,a1),gcd(ai+1,a2),...,gcd(ai+1,ai))。而这 i 个数里的每一个都是 ai+1 的约数,所以它们的 lcm 结果还是 ai+1 的约数,也就是范围是 1018 的。那我们暴力地把 igcd 都算出来再求 lcm 即可。

这样得到一个 O(n2logV) 的做法,显然是无法通过 n5000 的数据的。

这个做法的问题就是没用充分利用到前面已经算出来的信息。事实上算出 gcd(x,ai+1) 以后,我们令 bi+1=ai+1gcd(x,ai+1)。则 j=1ibj 就是 a1,a2,...,ai 的 lcm。相当于我们把 lcm 这个东西化成了乘积这样简单的形式。那想求 gcd(jibj,ai+1) 其实是容易的:因为 gcd(a,b)=gcd(amodb,b)。我们只需要算 jibjai+1 取模的结果,最后求一次 gcd 就可以得到 bi+1

此时得到一个 O(n2+nlogV) 的做法,就可以通过了。

如果只到这里肯定是不够写一篇的。我们来做这样一个问题:

给出 n 个数 a1,a2,...,an。对每个区间 [l,r] ,求 lcm(al,al+1,...,ar)998244353 取模的结果。

数据范围还是 n5000,ai1018

套用上面的做法对每个左端点跑只能做到 O(n3+n2logV) 的复杂度。我们的核心思路还是找到合适的 bi 使得 lcm 能被表示成 bi 的乘积的形式。当然你肯定找不到一组 bi 满足对于任何的 [l,r] 都有 bl×...×br=lcm(bl,...,br),哪有这么好的事情?考虑不断往序列后追加一个元素。然后 bi 只需要满足后缀 bi 的乘积是后缀的 lcm,拥有这个事情就足够了。

比如说我们添加了一个 an+1=v 这样的元素,显然有 bn+1=v,怎么修改 b1bn 呢。从后往前枚举 bi,令 g=gcd(v,bi),则我们会把 biv 都是除以 g,然后接着考虑 i1。朴素地做这样一个去更新 bi,就得到一个 O(n2logV) 的做法。瓶颈原因还是求 gcd 次数太多。

ci 是枚举到 bi 的时候此时 gcd(bi,v) 的值,也就是说 bi 要除以多少。那 j=incj 其实就是 gcd(j=inbj,v) 啊。后面这个形式看着就很眼熟。我们算出 bj 的后缀积 sjv 的值,这可以 O(n) 算出。那 j=incj 就是 gcd(sj,v)。这有什么用呢?因为 c1 的位置只有 O(logV) 处,如果我们能找到这些位置,就可以把瓶颈处的 O(n2logV) 降下来,不用求那么多次 gcd 了。

gcd(si,v)gcd(si+1,v) 的充要条件是 si+1modgcd(si,v)0。(这是因为 si+1si 而产生的性质)。说人话就是如果我们知道了 gcd(si,v) 的值,然后我们已经知道了 si+1modv 的值了,那就可以 O(1) 判断 gcd(si+1,v) 是否和 gcd(si,v) 相等。那就可以 O(1) 判断 ci 是否 >1。那第一步算一下 gcd(s1,v) 就行了啊。

这样我们只用求 nlogVgcd。不过大家应该都知道对一个数重复替换成他和另一个数的 gcd,这样一个过程均摊还是 O(logV) 的。所以总复杂度就是 O(n2+nlogV)。就算实现的菜了一点变成了 nlog2V 也没问题,反正他不是瓶颈。

posted on   Cry_For_theMoon  阅读(166)  评论(2编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
 
点击右上角即可分享
微信分享提示