题解
(没有胡乱分析是因为考场上死磕第三题去了 )
20pts
小于等于两千,说明n2可行,枚举所有区间即可。
(我咋就这么傻呢 )
40pts
随机生成数据,说明你只需要枚举左端点,因为你加不了几个gcd就变成1了~
60~80pts
我们刚才的思维都集中在如何统计每个区间内的gcd,当然这也是正解思路。不过,我们要考虑更为高效的方法。
考虑刚才的做法,我们时间主要都花在计算每个区间的gcd上了。但实际上,如果我们确定一个左端点,右端点不断右移,那整个区间的gcd只可能变小你变大一个给我看看 。
对于一个数n,它最多会有logn个质因数,那这说明啥?说明左端点不动,右端点慢慢挪的过程中,统计到的所有区间最多会出现logn个不同的gcd。
进一步分析,也就是当右端点在某些区间挪动的时候,gcd是不会改变的,而这些区间最多只有logn个。
这不就简单了吗?我们只需要找到区间与区间的分界点,就可以轻松算出以一个数为左端点的所有区间的所有gcd是哪些了。
我们考虑二分这些分界点,然后找个数据结构来查询一下从l到二分点的gcd,如果与上一个区间的gcd不一样,就把二分点往左挪,如果跟上个区间的一样就往右挪,直到刚好找到分界点。
这样的算法复杂度是nlog2n的,n个左端点 + 二分的log + 最多log个分界点。
如果我们考虑用线段树来维护区间信息,每次查询有个log,算gcd还有个log,一共就是nlog4n,60pts.
当然,由于没有修改操作,我们可以用st表省掉查询的log,也就是nlog3n,80pts。
100pts
我们来看看,这些log里面哪些可以省掉呢?
首先gcd的log不可能省,要不你算都算不出gcd。log+1。
然后查询也不可能省,你不查询怎么知道区间gcd的。log+1。
然后就是二分一个log,分界点一个log,貌似都不能丢,因为丢掉一个另一个就没用了啊。
那我们一起丢了吧!
别说,正解还真就一起丢了。惊不惊喜,意不意外!
为啥?
来来来,我们考虑一下。如果我们的左端点从最右边开始枚举,那左端点向左移动的过程中,分界点有什么变化呢?
如果你手动计算一下,你就会发现,分界点如果增加,只会出现在这一次的L和上一次的L之间;而对于之前的那些分界点,有的甚至会消失掉,但是绝对没有增加。
其实很好理解,当你新枚举的l和l-1取了gcd之后,这个gcd肯定会是L-1的因数。L-1的因数和l相比,前者的因数最多跟L一样(1*L),所以分界点最多也就是原来以L为左端点的时那么多;而如果L-1的因数变少了,分界点的个数也自然会减小。
但无论如何,可以保证之前确定的分界点的个数是不可能变多的。
所以呢,我们用个链表存下每个分界点,当l向左移动时,我们判断一下这个分界点还有没有用,要是没用就丢了(因为这一次没用以后肯定也没用了),这样就可以在O(1)的时间内找到所有分界点了。
emm,就这样,我们丢了两个log,再也不用辛苦的枚举分界点啦!
最终复杂度O(nlog2n)。
总结
跟上次长者的分析其实差不多,我们要的就是找到思路,然后寻找规律,考虑剪枝等等,最后把算法的复杂度弄到令人满意的范围内。
当然,从线段树和ST表的得分不一样来看,数据结构学的越多,越要弄清在什么时候该用什么结构,不要强行给自己增加时间复杂度,比如倍增能实现的LCA非要去用树链剖分,快排能弄好的非要用优先队列……多个log可能就丢了20分啊~