【qdu区域赛备战——容斥原理】A.Eddy's爱好

题目内容:
 Ignatius 喜欢收集蝴蝶标本和邮票,但是Eddy的爱好很特别,他对数字比较感兴趣,他曾经一度沉迷于素数,而现在他对于一些新的特殊数比较有兴趣。 
这些特殊数是这样的:这些数都能表示成M^K,M和K是正整数且K>1。 
正当他再度沉迷的时候,他发现不知道什么时候才能知道这样的数字的数量,因此他又求助于你这位聪明的程序员,请你帮他用程序解决这个问题。 
为了简化,问题是这样的:给你一个正整数N,确定在1到N之间有多少个可以表示成M^K(K>1)的数。 
输入描述
本题有多组测试数据,每组包含一个整数N,1<=N<=1000000000000000000(10^18). 

输出描述
对于每组输入,请输出在在1到N之间形式如M^K的数的总数。 
每组输出占一行。 

输入样例
10
36
1000000000000000000

输出样例
4
9
1001003332

一开始想了个法儿,从2开始循环判断每个数字,如果可以形成m的k次方就return true计数,但是这样会超时的

#include <iostream>
#include <cmath>
using namespace std;
typedef long long ll;
bool judge(ll n){
	for(ll i = sqrt(n); i >= 2; i--){
		ll temp = i * i;
		while(1){
			if(temp == n) return true;
			else if(temp > n) break;
			else if(temp < n) temp = temp * i;
		}
	}
	return false;
}
int main(){
	ll n;
	int count;
	while(cin>>n){
		count = 1;
		for(ll ii = 2; ii <= n; ii++){
			if(judge(ii)){
				cout<<ii<<endl;
				count++;
			}
		}
		cout<<count<<endl;
	}
}

看了一下容斥原理,然而不是很懂该怎么应用在算法里

看了一个大牛的博客,看了一下午!!!终于搞懂了!!!

#include <iostream>
#include <cmath>
using namespace std;


typedef long long ll;
int count = 0;
int i;
ll n;
int sushu[60] = {2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59}; //17个素数,因为2的60次方超范围了
/*
容斥原理 
我首先是这样想的,我们算1 - n之间的满足条件的数的个数,要算
以2,3,4,5,6……为指数时有多少底数满足,然后相加
这样的想法显然是错的呀,因为有很多重复eg. 4^2 = 2^4
然后我们发现,M^k = M^(k' * p) p为质数
就是说,如果指数为合数,就可以表示为一个质数和另一数的积,就可以转化为以这个质数为指数
	 
所以我们考虑以2,3,5,7,11……这样的质数为指数时有多少底数满足,然后相加
但是还是会有重复,因为8^2 = 4^3 因为他们都是2^6,所以我们要将 指数是两个质数的积的数减去
根据容斥定理,应该将以一个质数做指数的时候的所有情况相加 -  以两个质数的积作指数的情况相加 + 以三个质数为积的指数的情况相加
(由于^60 > 10^18,所以只考虑小于60的质数,又因为2*3*5*7 > 60,所以只考虑三个质数的积为指数的情况) 
*/ 
void dfs(int z, int num, int p){//z是指数数组的下标 
	if(p == 0){
		ll t = pow(n, 1.0 / num);//计算当num为指数时,底数最大为多少 
		if(pow(t, 0.0 + num) > n) t--;//如果底数的num次方大于n就减掉这种情况 
		t--;//由于1在每个集合里都符合所以先在计算时减去,再在cout中加上 
		if(t > 0)
			count += t * ((i & 1) ? 1 : -1);//位与运算判断奇偶性,如果是1个质数和三个质数就+,2个质数的积就减 
		
		return;
	}
	if(z >= 17) return;//如果下标过了16就终结 
	if(num * sushu[z] < 60)//如果指数还在范围内就向下搜 
		dfs(z + 1, num * sushu[z], p - 1);
	dfs(z + 1, num, p);
}
int main(){	 
	while(cin>>n){
		count = 0;
		for(i = 1; i <= 3; i++){
			dfs(0,1,i);//i表示构成指数的素数的个数 
		}
		cout<<count + 1<<endl;//加上1这种情况 
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值