首页 > 代码库 > [VIJOS1889]天真的因数分解

[VIJOS1889]天真的因数分解

题目:天真的因数分解

传送门:https://vijos.org/p/1889

题目描述:

  小岛: 什么叫做因数分解呢?

  doc : 就是将给定的正整数n, 分解为若干个素数连乘的形式.

  小岛: 那比如说 n=12 呢?

  doc : 那么就是 12 = 2 * 2 * 3 呀.

  小岛: 呜呜, 好难, 居然素数会重复出现, 如果分解后每一个素数都只出现一次, 我就会.

  wish: 这样来说, 小岛可以正确分解的数字不多呀.

  doc : 是呀是呀.

  wish: 现在问题来了, 对于给定的k, 第 k 个小岛无法正确分解的数字是多少?

分析:

  (1)很明显,本题要你求出第N个mobius[i]==0的数。算法一: 求出mobius函数后回答询问,可以得30分。

  (2)考虑函数$f_x=\sum_{i \leq x \&\& mobius[i]==0}1$的值,显然,这个函数满足单调性。算法二:可以二分x,询问$f_x$,比较k与$f_x$的大小。

    (2.1)如何快速求$f_x$?考虑容斥原理。$f_x=\sum_i -mobius[i]*{n \over {i^2}}$。显然$i \leq \sqrt{n}$。令$mo[i]=mobius[i]$即如果有平方因子,赋值为0,否则有奇数个因子赋值为1,偶数个赋值为-1。使用线性筛法求mobius函数。

    (2.2)二分的上界可以试一试,这里直接提供:(25505460948)。

代码:

 1 #include <cstdio>
 2 #include <iostream>
 3 #define LL long long
 4 const LL INF=25505460948ll;
 5 bool v[1000005];
 6 int p[1000005],mo[1000005];
 7 void GetMo(){
 8     const int N=1000000;
 9     for(int i=2;i<=N;++i){
10         if(!v[i]){p[++p[0]]=i;mo[i]=1;}
11         for(int j=1;j<=p[0] && i*p[j]<=N;++j){
12             v[i*p[j]]=true;
13             if(i%p[j]==0){mo[i*p[j]]=0;break;}
14             mo[i*p[j]]=-mo[i];
15         }
16     }
17 }
18 LL check(LL x){
19     LL sum=0;
20     for(LL i=1;i*i<=x;++i)
21         sum+=mo[i]*x/(i*i);
22     return sum;
23 }
24 int main(){
25     //freopen("in.txt","r",stdin);
26     GetMo();
27     LL k,l=0,r=INF,mid;
28     scanf("%lld",&k);
29     for(;l<=r;){
30         mid=(l+r)>>1;
31         if(check(mid)<k)
32             l=mid+1;else r=mid-1;
33     }
34     printf("%lld",l);
35     //fclose(stdin);
36     return 0;
37 }

 

[VIJOS1889]天真的因数分解