[BZOJ2440] 完全平方数 莫比乌斯函数+容斥+二分

题目传送门:【BZOJ 2440】


题目大意:多组数据,求第 k 个不含平方数因子的数。(T ≤ 50,1 ≤ k ≤ 10 9

题目分析:
一看题目中求出不含平方数因子的数,马上就能想到,它很有可能和 μ(x) != 0 有关。(这里的 x 为任意一整数)

由题,由于 k 最大为 10 9 ,线性算法(例如暴力扫一遍求出第 k 个这样的数)时间复杂度无法承受,于是考虑二分。这时,我们需要对题目进行转化。

设第 k 个不含平方数因子的数为 p,根据题意,易得 p ≥ k。又因为在前 2k 个数中,含有平方数因子的数的个数不超过 2k22 + 2k32 + 2k42 +……+ 2kn2 k ( 其中 n22k);所以 p ≤ 2k。

证明:

2k22+2k32++2kn2=2k122+132++1n2

2k(113+124+135++1(n1)(n+1))

裂项相消后得
2k12(113+1214+1315++1n21n+1n11n+1)

k(32+1(n1)(n+1))

由于 1(n1)(n+1) 在 n ≥ 2时单调递减且恒大于 0,所以原式 > 3k2 ,即原式 > k

所以:2k22+ 2k32 + 2k42 +……+ 2kn2 ≥ k,可得 p ≤ 2k,证毕。


所以,我们可以对 p 在 [k,2k] 内进行二分操作,然后看它是否等于 k。

根据容斥原理,满足要求的 ans = p -(只有一个质数因子次数 ≥ 2的个数)+(只有2个质数因子 ≥ 2的个数)- ……,经转化后即为 pi=1μ(i)pi2 。此时即为最终的答案表达式。

下面附上代码:

  1. #include<cstdio>  
  2. const int MX=45005;  
  3. int prime[MX],miu[MX],ptot,k;  
  4. bool isnot[MX];  
  5.   
  6. void get_miu(int n){                            //线性筛求miu值   
  7.     isnot[1]=true;  
  8.     miu[1]=1;  
  9.     for (int i=2;i<=n;i++){  
  10.         if (!isnot[i]){  
  11.             prime[++ptot]=i;  
  12.             miu[i]=-1;  
  13.         }  
  14.         for (int t=1;t<=ptot;t++){  
  15.             int j=prime[t]*i;  
  16.             if (j>n) break;  
  17.             isnot[j]=true;  
  18.             miu[j]=miu[prime[t]]*miu[i];  
  19.             if (i%prime[t]==0){  
  20.                 miu[j]=0;  
  21.                 break;  
  22.             }  
  23.         }  
  24.     }  
  25. }  
  26. bool check(int m){  
  27.     long long tot=0;  
  28.     for (int i=1;i*i<=m;i++){  
  29.         tot+=miu[i]*(m/(i*i));                  //根据容斥原理计算   
  30.     }  
  31.     return tot>=(long long)k;  
  32. }  
  33.   
  34. int main(){  
  35.     get_miu(45000);  
  36.     int T;  
  37.     scanf(”%d”,&T);  
  38.     while (T–){  
  39.         scanf(”%d”,&k);  
  40.         long long lf=k,rt=2*k,mid;  
  41.         while (lf<rt){  
  42.             mid=(lf+rt)>>1;  
  43.             if (check(mid)) rt=mid;  
  44.             else lf=mid+1;  
  45.         }  
  46.         printf(”%lld\n”,lf);  
  47.     }  
  48.     return 0;  
  49. }  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值