P1889天真的因数分解
描述
小岛: 什么叫做因数分解呢?
doc : 就是将给定的正整数n, 分解为若干个素数连乘的形式.
小岛: 那比如说 n=12 呢?
doc : 那么就是 12 = 2 X 2 X 3 呀.
小岛: 呜呜, 好难, 居然素数会重复出现, 如果分解后每一个素数都只出现一次, 我就会.
wish: 这样来说, 小岛可以正确分解的数字不多呀.
doc : 是呀是呀.
wish: 现在问题来了, 对于给定的k, 第 k 个小岛无法正确分解的数字是多少?
格式
输入格式
输入只有一行, 只有一个整数 k.
输出格式
输出只有一行, 只有一个整数, 表示小岛无法正确分解出来的第k个数字.
限制
对于30%的数据, k <= 2,000,000
对于100%的数据, 1 <= k <= 10,000,000,000
提示
前 10 个小岛无法正确分解出来的数字依次是: 4 8 9 12 16 18 20 24 25 27
题解:
莫比乌斯反演+二分+容斥
先预处理出来 莫比乌斯函数,但是要反过来,也就是如果有平方因子,赋值为0,否则有奇数个因子赋值为1,偶数个赋值为-1
那么x以内包含i*i因子的个数为x/(i*i),根据容斥原理,x以内所有的含平方因子的数字有sum(x / (i * i) * Mob[i])个
so,这样加加减减就算出了x以内有多少个这样的数
然后二分答案就好
代码:
#include
#define maxn (1000000 + 10)
typedef long long int LLI;
using namespace std;
bool isPrime[maxn];
LLI primeList[maxn],primeCount = 0;
short Mob[maxn];
void Mobius(LLI n) {
memset(isPrime,true,sizeof(isPrime));
isPrime[0] = false;
isPrime[1] = false;
Mob[1] = 0;
for(int i = 2; i <= n; i ++) {
if(isPrime[i]) {
primeCount ++;
primeList[primeCount] = i;
Mob[i] = 1;
}
for(int j = 1; j <= primeCount; j ++) {
if(i * primeList[j] > n) break;
isPrime[i * primeList[j]] = false;
if(!(i % primeList[j])) {
Mob[i * primeList[j]] = 0;
break;
} else Mob[i * primeList[j]] = -Mob[i];
}
}
}
LLI sum(LLI x) {
LLI re = 0;
for(LLI i = 1; i * i <= x; i ++) {
re = re + x / (i * i) * Mob[i];
}
return re;
}
int main() {
// freopen("in.txt","r",stdin);
Mobius(maxn);
LLI k;
scanf("%lld",&k);
LLI l = 0,r = 25505460948LL,mid,ans;
while(l <= r) {
mid = (l + r) >> 1;
if(sum(mid) >= k) ans = mid,r = mid - 1;
else l = mid + 1;
}
printf("%lld\n",ans);
return 0;
}