Eddy's爱好 HDU - 2204(容斥原理 莫比乌斯函数)

该博客主要讲述了如何利用容斥原理和莫比乌斯函数来解决一个关于寻找特定形式的数字个数的问题。作者通过枚举K值,发现莫比乌斯函数的相反数与各K值对应的系数相关,从而构建了解决方案。文章提到了代码实现,并提到初次尝试时由于未考虑1的情况,结果总是比正确答案大1,但意外地发现减1后得到正确答案,作者对此感到疑惑并寻求帮助。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:寻找1 ~ N中能表示成 M K M^K MK,其中M和K都是正整数并且K>1的这样的数的个数。

思路: N很大,枚举N是不可能了。但它既然表示成 M K M^K MK,我们就枚举K就好了。因为除了1,M最小也就取到2,而2的64次方就已经超过N的范围了。所以K最大就取到64。至于具体怎么枚举,我们可以让K从2循环到64,当K=2时,满足情况的M有1,2,… , N \sqrt{N} N ,一共就这 N \sqrt{N} N 个,因为再大的话乘方之后就大于N了。当K=3时,满足情况的M有1,2,……, N 3 \sqrt[3]{N} 3N ,一共有 N 3 \sqrt[3]{N} 3N 个。同理,K等于4,5,… ,64时,满足情况的M分别就有 N 4 , N 5 , … , N 64 \sqrt[4]{N},\sqrt[5]{N},… ,\sqrt[64]{N} 4N 5N 64N 个。但我们要注意,当我们把K=2的情况都加上之后,K=4的情况就没必要再加了。因为 N 4 = N 2 2 \sqrt[4]{N}=\sqrt[2]{\sqrt[2]{N}} 4N =22N 。同理,当K=6的时候, N 6 = N 3 2 = N 2 3 \sqrt[6]{N} = \sqrt[2]{\sqrt[3]{N}}=\sqrt[3]{\sqrt[2]{N}} 6N =23N =32N ,所以我们找K=2,K=3时相当于都把K=6的情况多找了一遍,因此我们要把这种情况减掉。所以最后的答案就是 N 2 + N 3 + 0 ∗ N 4 + N 5 − N 6 … \sqrt[2]{N} + \sqrt[3]{N} + 0*\sqrt[4]{N} + \sqrt[5]{N} - \sqrt[6]{N}… 2N +3N +04N +5N 6N ,看它们的系数是不是很熟悉?没错,就是莫比乌斯函数的相反数,因此我们可以先离线把前64位的莫比乌斯函数筛出来,然后用它的相反数乘上每一项的个数加起来就是最后的答案了。不过因为每一次都算上了1,我们可以先将答案初始化成1,就是先把1加上,之后的每一项就都把1减掉就可以了。刚开始的时候我没考虑到1,就直接算了,巧合的是结果都比答案大1,我直接用我算的结果减1交上,还真对了。但我没想明白为什么,希望路过的大佬可以帮帮我,QAQ

下面是我的代码

#include <iostream>
#include <cstdio>
#include <cmath>

using namespace std;

typedef long long int ll;

const int maxn = 199;

ll mu[maxn + 10], prime[maxn + 10];
bool vis[maxn + 10];

void Moblus()
{
	mu[1] = 1;
	for(int i = 2; i < maxn; ++ i)
	{
		if(!vis[i])
		{
			prime[++ prime[0]] = i;
			mu[i] = -1;
		}
		for(int j = 1; j <= prime[0] && i * prime[j] < maxn; ++ j)
		{
			vis[i * prime[j]] = true;
			if(i % prime[j] == 0)
			{
				mu[i * prime[j]] = 0;
				break;
			}
			else
			{
				mu[i * prime[j]] = -mu[i];
			}
		}
	}
}

int main()
{
	//freopen("in.txt", "r", stdin);
	ll n, ans;
	Moblus();
	while(cin >> n)
	{	
		ans = 1;
		for(ll i = 2; i <= 64; ++ i)
		{
			ans -= mu[i] * ((ll)pow(n, 1.0 / i) - 1);
		}
		cout << ans << endl;
//		ans = 0;            //这是我第一次写的,也AC了。
//		for(int i = 2; i <= 64; ++ i)
//		{
//			ans -= (mu[i] * (ll)pow(n, 1.0 / i));
//		}
//		cout << ans - 1 << endl;
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值