void pr(int k) //求k的质因子
{
num = 0;
for (int i = 2 ; i * i <= k ; i++)
{
if (k % i == 0)
{
p[num++] = i;
while (k % i == 0)
k /= i;
}
}
if (k > 1)
p[num++] = k;
}
我们首先把k分解质因数,储存到p数组中,num表示质因子的数量。
然后用容斥原理,我们反着求不与k互质的数的个数,到时候一减就得出结果了。
举个例子,比如 k 的质因子有 2,3,5。那么2、3、5的倍数都不和 k 互质,另外还没有完,可能有重复的地方,比如6,既是2的倍数又是3的倍数,前面用 k/2 + k/3 的时候多减了,这个时候要加上 k / (2*3)。同理,10,15这一类数都应该加上。但是还有类似于30这样的数,它是2,3,5的倍数,减的时候又多减了。
然后我们会发现,出现奇数个数,就用加法,偶数个数用减法。
最后的式子是这样的:k / 2 + k / 3 + k / 5 - k / (2 * 3) - k / (3 * 5) - k / (2 * 5) + k / (2 * 3 * 5)
有点长,看一下容易发现我说的奇偶的规律。
但是要怎么取算这个又出现问题了。
这里提供二进制的方法,个人觉得比较好理解。
设质因数的个数为m。
有一个浮动的数字,从1 ~ m依次递增,它的二进制的每一位表示用了哪些数字,比如5(101),其二进制的第一位和第三位(倒着数)是1,则它表示用了第一个质因数和第三个质因数。就是这个意思,这样就能发现:从1到m遍历一遍,就把所有的可能都包括了。
代码:
__int64 solve(__int64 n) //1~n中不与k互质的数
{
__int64 ans = 0;
for (__int64 i = 1; i < (__int64)1 << num ; i++) //其二进制位为1,表示这些质因数被用到
{
int ant = 0; //用奇数个质因数加,偶数个减
__int64 t = 1;
for (int j = 0 ; j < num ; j++)
{
if (((__int64)1 << j) & i)
{
t *= p[j];
ant++;
}
}
if (ant & 1) //奇数加
ans += n / t;
else //偶数减
ans -= n / t;
}
return ans;
}