题意:给出n个数,区间为(1,n),查询区间(l,r)的GCD,以及区间(1, n)有多少子区间的GCD等于区间(l,r)的GCD。
做法:使用线段树,或RMQ维护区间GCD,并且要预处理出所有可能出现的GCD值 的区间数量。针对固定左端点,一直向右GCD阶梯状减小的特性,大概有两种做法。
1)对于某一固定起点(以选定左端点L=i为例),从最右端R开始,二分枚举GCD突变位置pos,这一段阶梯的长度就是R-pos+1,定义INTERGCD(L,R)=区间(L,R)的GCD,则GCD值等于 INTERGCD(L,R)的区间数量就增加R-pos+1。
对于左端点i:令L=i,R=n。
a、l=L,r=R。
b、m=(l+r)/2,如果INTERGCD(L,m)<=INTERGCD(L,R),r=m;否则l=m+1;如果l==r,则阶梯突变位置就是l或r,记录pos=l,转c;否则重复b;
c、阶梯长度就是pos-R+1,则_map[INTERGCD(L,R)]+=pos-R+1;令R=pos-1,如果R>=L,转a,计算下一个阶梯;否则,以i为左端点的阶梯就计算完了。
2)以arr=(1,2,4,6,7)这列数为例,如图,数列下面是以某个i为端点的所有的GCD值,_tmp和_tmpnext是值为的map,_tmp记录当前列,各个GCD值的数量,_tmpnext记录下一行的_tmp值,_tmpnext由_tmp推得;对于第i-1列的_tmp的每一个元素iter(指针),_tmpnext[GCD(arr[i],iter->first)]+=iter->second。图中给出了两列所对应的_tmp(_tmpnext)的值,(可能第四列到第五列理解更清晰)。
对于第一种写法,因为在预处理的时候要询问INTERGCD(l,r),所以用线段树,预处理的时间复杂度就成了O(n(logn)^3),就会超时。用RMQ,O(1)的查询就可以了。第二种写法在处理的时候不需要询问INTERGCD,就无所谓了。
附上两种写法:calcu_interval是第一种写法,progress是第二种。
RMQ
#include
#include
#include
#include
#include
#include
线段树
#include
#include
#include
#include
#include
#include