bzoj 2440
题意:有一个从小到大的由不包含平方约数的数组成的数列,从1开始,求第k项。
“满足某种限制的数的第k个”+二分答案="前n个数有多少个数满足限制“
求[1,n]中有多少个数没有平方约数,我们考虑求满足要求的数的补集。
求[1,n]中有多少个数有平方约数,我们考虑枚举约数后用容斥解决。
设Ai为包含[1,n]中所有为pi*pi的倍数的数的集合,因为一个数存在平方约数当且仅当它是某个(可能不止一个)质数的平方的倍数,所有转换后的问题的答案是(假如1~sqrt(n)只有3个质数):
|A1 U A2 U A3 ... | = |A1|+|A2|+|A3|-|A1 and A2|-|A1 and A3|-|A2 and A3|+|A1 and A2 and A3|
我们发现是奇数个质数的积的倍数前面的符号都是-1,偶数个则是1,这正好符合Mobius函数的定义,于是我们可以枚举所有不包含平方因子的数i,然后floor(n/(i*i))为是它的倍数的数的个数,而它们前面的符号为mobius[i]。
1 /************************************************************** 2 Problem: 2440 3 User: idy002 4 Language: C++ 5 Result: Accepted 6 Time:1232 ms 7 Memory:1232 kb 8 ****************************************************************/ 9 10 #include <cstdio> 11 #include <cmath> 12 13 int prm[6000], isnot[50010], mu[50010], ptot; 14 15 void init() { 16 mu[1] = 1; 17 for( int i=2; i<=50000; i++ ) { 18 if( !isnot[i] ) { 19 prm[++ptot]=i; 20 mu[i] = -1; 21 } 22 for( int j=1; j<=ptot && prm[j]*i<=50000; j++ ) { 23 isnot[i*prm[j]]=true; 24 if( i%prm[j]==0 ) { 25 mu[i*prm[j]]=0; 26 break; 27 } 28 mu[i*prm[j]]=-mu[i]; 29 } 30 } 31 } 32 int calc( int n ) { 33 int rt = 0; 34 int maxi = (int)ceil(sqrt(n)); 35 for( int i=1; i<=maxi; i++ ) 36 rt += mu[i]*(n/(i*i)); 37 return rt; 38 } 39 int nth( int k ) { 40 int lf=1, rg=2000000000; 41 while( lf<rg ) { 42 int mid=lf+((rg-lf)>>1); 43 int cnt=calc(mid); 44 if( cnt<k ) lf=mid+1; 45 else rg=mid; 46 } 47 return lf; 48 } 49 50 int main() { 51 int T; 52 init(); 53 scanf( "%d", &T ); 54 while( T-- ) { 55 int k; 56 scanf( "%d", &k ); 57 printf( "%d\n", nth(k) ); 58 } 59 }