洛谷4318 完全平方数

题 意 : 题意: :
求 第 k 个 不 含 平 方 因 子 的 数 求第k个不含平方因子的数 k


题 解 : 题解: :

我 们 可 以 题 目 转 为 求 给 定 n , 求 n 以 内 的 无 平 方 因 子 数 的 个 数 , 然 后 二 分 得 到 结 果 \qquad我们可以题目转为求给定n,求n以内的无平方因子数的个数,然后二分得到结果 nn
求 n 以 内 的 无 平 方 因 子 数 的 个 数 , 即 求 求n以内的无平方因子数的个数,即求 n
∑ k = 1 n μ 2 ( k ) , 这 里 μ 是 莫 比 乌 斯 函 数 \sum_{k=1}^{n}\mu^{2}(k), 这里\mu是莫比乌斯函数 k=1nμ2(k),μ

我 们 考 虑 一 个 素 数 p , 那 么 p 的 倍 数 都 含 有 平 方 因 子 , 个 数 是 ⌊ n p 2 ⌋ , 需 要 从 答 案 中 去 掉 。 但 是 这 样 会 多 去 掉 一 些 数 , 比 如 对 于 不 同 的 素 数 p 1 , p 2 ,    p 1 2 ⋅ p 2 2 的 倍 数 就 被 去 掉 了 两 次 , 个 数 是 ⌊ n p 1 2 ⋅ p 2 2 ⌋ , 需 要 加 回 来 , 这 里 就 是 使 用 了 容 斥 原 理 . \qquad我们考虑一个素数p, 那么p^的倍数都含有平方因子,个数是 \lfloor \frac{n}{p^2} \rfloor , 需要从答\\ 案中去掉。但是这样会多去掉一些数,比如对于不同的素数p_{1}, p_{2}, \ \ p_{1}^{2} \cdot p_{2}^{2}的\\倍数就被去掉了两次,个数是 \lfloor \frac{n}{p_{1}^{2} \cdot p_{2}^{2}} \rfloor , 需要加回来,这里就是使用了容斥原理. ppp2n,p1,p2,  p12p22p12p22n,,使.

如 果 d 是 s 个 不 同 素 数 的 乘 积 , 那 么 对 答 案 的 贡 献 就 是 ( − 1 ) s ⌊ n d 2 ⌋ , 如 果 d 含 有 平 方 因 子 , d 对 答 案 没 有 贡 献 , 也 就 是 说 容 斥 的 系 数 就 是 M o b i u s 函 数 \qquad如果d是s个不同素数的乘积, 那么对答案的贡献就是(-1)^s \lfloor \frac{n}{d^2} \rfloor, 如果d含有平方因\\子,d对答案没有贡献,也就是说容斥的系数就是Mobius函数 ds,(1)sd2n,dd,Mobius

因 此 可 以 有 下 面 的 结 论 , 答 案 就 是 因此可以有下面的结论,答案就是
∑ k = 1 n μ 2 ( k ) = ∑ d = 1 n μ ( d ) ⌊ n d 2 ⌋ \sum_{k=1}^{n}\mu^{2}(k) = \sum_{d=1}^{\sqrt{n}} \mu(d) \lfloor \frac{n}{d^2} \rfloor k=1nμ2(k)=d=1n μ(d)d2n

枚 举 d , 预 处 理 莫 比 乌 斯 函 数 即 可 \qquad枚举d,预处理莫比乌斯函数即可 d


代 码 : 代码: :

#include <bits/stdc++.h>
using namespace std;


#define Debug(x) cout<<#x<<"="<<x<<endl;
typedef long long LL;

const int N = 100100;
int n, mu[N+50], p[N+50];
int tot;
bool used[N+50];

//预处理出素数 莫比乌斯函数
void init() {
	mu[1] = 1;
	for (int i = 2; i <= N; i++) {
		if (!used[i]) {
			mu[i] = -1;
			p[++tot] = i;
		}
		for (int j = 1; j <= tot && p[j] * i <= N; j++) {
			used[p[j] * i] = true;
			if (i % p[j] == 0) {
				mu[i * p[j]] = 0;
				break;
			}
			mu[i * p[j]] = mu[i] * (-1);
		}
	}
	
}


LL check(LL n) {
	LL res = 0;
	for (int i = 1; i * i <= n; i++) {
		res += mu[i] * n / (i * i);
	}
	return res;
}


LL cal(LL k) {
	LL l = 1, r = k * 2;
	LL ans;
	while (l <= r) {
		int mid = (l + r) / 2;
		int number = check(mid);
		if (number >= k) r = mid - 1, ans = mid;
		else l = mid + 1;
	}
	return ans;
}


int main() {
	int T;
	cin >> T;
	init();
	while (T--) {
		int x;
		cin >> x;
		cout << cal(x) << endl;
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值