「雅礼day2」最大公约数gcd 题目 分析 代码 思路分析
有 (n) 个正整数 (x_1…x_n) ,初始时状态均为未选。有 (m) 个操作,每个操作给定一个编号 (i) ,将 (x_i) 的选取状态取反。每次操作后,你需要求出选取的数中有多少个互质的无序数对。
(n,mle 10^5)
分析
刚开始难免想到直接每次枚举因数集合来做,找到每次的变化量,但是这样好像需要子集反演,时间复杂度直接爆炸。(这样做是 (n imes n^{log n}=n^2) 的)
然后就不会了。
实际上是维护了三个数组来实现维护答案(其实只维护了一个,另外两个都是现算的),不太能明白是怎么想到的。
也就是维护 (f(x)) 表示最大公因数是 (x) 的对数,(g(x)) 表示最大公约数是 (x) 的倍数的对数,(t(x)) 维护 (x) 的倍数的个数,显然第三个可以直接算第二个,第二个可以反演得到第一个,结束,维护第三个和反演第一个的过程是根号级别的。
代码
口胡的,可以看pzy的代码
思路分析
一点猜测思考的正确历程:
可能是套路,也就是维护一个数组表示 (gcd) 是某一个数的这样的对数,写出来就是:
[large f(k)=sumlimits_{i=1}^nsum_{j=i+1}^n[gcd(a_i,a_j)=k] ]这个柿子似乎并不好求,甚至是某道题的加强版(那道题是求 (large sumlimits_{i=1}^nsumlimits_{j=1}^n[gcd(i,j)=1]) ,当时俺做的时候是在欧拉函数里的,虽然莫反比这个做法高到不知道哪里去了)
其实这道题也是求 (f(1)) 。
然后呢,想到 (f) 数组直接不好求(因为 (i,j) 都只是数组下标怎么维护嘛),于是开始想反演(能正好想的是莫比乌斯反演可能是因为反演就那些......?),通过反演回来的柿子求 (f) ,于是设某个 (g) 数组有演绎:
[large g(k)=sumlimits_{dvert k}f(k) ]或者超集形式:
[large g(k)=sumlimits_{kvert d}f(d) ]当然,直接维护求和柿子当然不可能(不然要反演干什么,这样还变复杂了)
于是考虑其本身柿子代表的意义,发现两者是 (gcd) 是 (k) 的因数的数对对数和 (gcd) 是 (k) 的倍数的数对对数。
前者似乎必须要枚举因数才能维护,而后者发现可以直接维护是 (k) 的倍数的这样数的个数即可。
(为什么会这样?可能是因为取 (gcd) 是一个对于质因子指数取 (min) 的过程,而我们知道两个数的 (min) 小于某个数,相比于知道两个数都是某个数的倍数,在这里显然是后者更好,并且这还是个非常强的条件,是个充要条件)
于是我们考虑通过 (g) 来反演得到 (f) ,这就简单多了,根据超集形式的莫比乌斯反演有:
[large f(k)=sumlimits_{kvert d}mu(dfrac dk)g(d) ]那么问题就变成求 (g) ,这个很好求吧!因为我们刚刚不是说到了我们现在已经有一个充分必要条件才使得两个数 (gcd) 为 (k) 吗!
直接把是 (k) 的倍数的所有数(当然是可以重复的)记录个数设为 (cnt),然后 (g(k)) 就是 (dbinom{cnt}{2}) 了呗。
这个而 (cnt) 又怎么维护呢?其实也非常好做!我们算上这里的修改,其实无非就是我们先时刻存着当前一个数的倍数个数,然后每次加入删除的时候遍历一下这个数的所有因数就行了!
因为因数个数是根号级别的,所以单次修改时间复杂度为 (O(sqrt{V}))