BZOJ 2440 - 容斥原理 + 莫比乌斯函数的应用
找规律题?(雾。。
第一道A掉的题号2开头的题(雾。。
题目要求的是第k个无平方因子数。。
直接求并不好搞,所以我们二分答案,转而求区间 [1..x] 上的无平方因子数的个数。
我们考虑sqrt(x)以内的质数。对于[1..x]内的整数,含有平方因子的数的数目应该排除。那么,我们减去每个质数的平方的倍数的个数(4的倍数、9的倍数等)。
但是,这肯定会造成重复减,比如36既被4减,又被9减。所以,我们应该加上由两个质数乘积的平方的数的数目(如36的倍数、100的倍数等)。
这个过程很像欧拉φ函数的推导过程,——用容斥原理类推即可。
但是问题在于,我们如何判断一个乘积平方的数目到底是加上还是减去呢?我们观察式子,很容易发现一个乘积a前面的符号就是μ(a)。这个结论由我们的推导过程也可以发现是显然的。
剩下来的过程就很自然了。x以内a^2的倍数有[x / (a^2)]个,所以ans(x) = sum { μ(a)*[x/(a^2)] | 1<=a<=sqrt(x) }
"这题和莫比乌斯反演没关系,算是莫比乌斯函数的一个应用吧。。。" ——Po姐
关于计算莫比乌斯函数,详见代码和注释。此外还可参考贾志鹏的论文。
// BZOJ 2440 #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> using namespace std; typedef long long LL; typedef unsigned long long uLL; const int sqrtX=50000+5, INF=0x7fffffff; #define rep(i,a,b) for (int i=a; i<=b; i++) #define read(x) scanf("%d", &x) #define fill(a,x) memset(a, x, sizeof(a)) int T, tot, miu[sqrtX], p[sqrtX]; bool vis[sqrtX]; LL ans, k; void get_Mobius_function() { miu[1]=1; tot=0; fill(vis, false); rep(i,2,sqrtX) { if (!vis[i]) p[++tot]=i, miu[i]=-1; // 一边计算μ函数一边筛质数p[] for (int j=1; j<=tot && p[j]*i<=sqrtX; j++) { vis[i*p[j]]=true; if (i%p[j]==0) { miu[i*p[j]]=0; break; } // i是p[j]的倍数,它们的乘积就会含有平方因子,因此μ值为0 // 此处break的原理类似于线性筛法。因为μ函数是积性函数,所以break可以保证每个数只被其最小的因子计算 else miu[i*p[j]]=-miu[i]; // 由于p[j]为质数,两个数的gcd为1,根据定义(质因子数目的奇偶),把乘积的μ函数取反即可 } } } LL cal(int x) { LL ret=0; for (int i=1; i*i<=x; i++) ret+=(x/(i*i)*miu[i]); return ret; } int main() { get_Mobius_function(); read(T); while (T--) { scanf("%lld", &k); LL l=k, r=INF; while (l<=r) { LL mid=(l+r)>>1; if (cal(mid)>=k) ans=mid, r=mid-1; else l=mid+1; } printf("%lld\n", ans); } return 0; }