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;
}




posted @ 2015-12-16 21:20  Armeria  阅读(433)  评论(0编辑  收藏  举报