『题解』Luogu-P4318 完全平方数

P4318 完全平方数

Description

  • 多测,\(T\) 组数据;
  • 每次给出一个整数 \(k\),求第 \(k\) 小不含平方因子的数(注意:\(1\) 不算平方因子);
  • \(1\le k\le 10^9, T\le 50\)。

Solution

\(n\) 不含平方因子意味着 \(\mu^2(n) = 1\),而且 \(1\) 也正好满足。

我们设

\[S(n) = \sum_{i = 1}^n \mu^2(i) \]

即前 \(n\) 个正整数中有多少个数不含平方因子。

那么考虑到 \(S\) 是单调不减的,直接二分即可,下限显然是 \(k\),上限可以写程序暴力算 \(k = 10^9\) 的情况,差不多是 \(2k\),这一步是 \(\log k\) 的。

于是问题转化成快速求 \(S\)。

\[f(n) = \mu^2(n) \]

首先 \(f\) 肯定是个积性函数,那么直接用杜教筛。

\[f * g = h \]

如果 \(h = \mathbf{1}\),那么

\[\begin{aligned} (f * g)(n) & = \sum_{d\mid n} f\left(\dfrac{n}{d}\right) g(d) \\ & = 1 \end{aligned} \]

考虑这样一个函数

\[g(n) = [n = m^2, m\in \mathbb{N}^*] \]

即 \(n\) 是否为完全平方数。

那么

\[(f * g)(n) = \sum_{d\mid n} f\left(\dfrac{n}{d}\right) g(d) \]

只有在 \(d\) 取 \(n\) 的最大平方因子时才有值 \(1\),即 \((f * g)(n)\) 恒为 \(1\)。

证明:

当 \(d\) 不为完全平方数时,\(g(d) = 0\);

若 \(d\) 为完全平方数但不为 \(n\) 的最大平方因子,则 \(\dfrac{n}{d}\) 必含大于 \(1\) 的平方因子,\(f\left(\dfrac{n}{d}\right) = 0\);

当 \(d\) 为 \(n\) 的最大平方因子时,\(\dfrac{n}{d}\) 必不含平方因子,有 \(f\left(\dfrac{n}{d}\right) = g(d) = 1\)。

那么

\[\begin{aligned} g(1) S(n) & = \sum_{i = 1}^n h(i) - \sum_{i = 2}^n g(i) S\left(\left\lfloor\dfrac{n}{i}\right\rfloor \right) \\ & = n - \sum_{i = 2}^n g(i) S\left(\left\lfloor\dfrac{n}{i}\right\rfloor \right) \end{aligned} \]

又因为 \(g(1) = 1\),所以

\[S(n) = n - \sum_{i = 2}^n g(i) S\left(\left\lfloor\dfrac{n}{i}\right\rfloor \right) \]

注意 \(g(i)\) 只有在 \(i = m^2\) 时才有值,所以

\[S(n) = n - \sum_{i = 2}^{\left\lfloor\sqrt{n}\right\rfloor} S\left(\left\lfloor\dfrac{n}{i^2}\right\rfloor \right) \]

注意到 \(\sqrt{n}\) 并不大,所以别用整除分块了,那玩意儿除法一大堆常数大得要死。

时间复杂度为 \(\Omicron(k^{\frac{2}{3}} + T \log k)\)。

Code

Warning : 注意二分时 \(l + r\) 有可能爆 \(\text{int}\)。

//18 = 9 + 9 = 18.
#include <iostream>
#include <cstdio>
#include <unordered_map>
#include <cmath>
#define Debug(x) cout << #x << "=" << x << endl
typedef long long ll;
using namespace std;

const int MAXN = 1e6 + 5;
const int N = 1e6;
typedef int arr[MAXN];

arr p, mu, sum;
bool vis[MAXN];

void pre()
{
	mu[1] = 1;
	for (int i = 2; i <= N; i++)
	{
		if (!vis[i])
		{
			p[++p[0]] = i;
			mu[i] = -1;
		}
		for (int j = 1; j <= p[0] && i * p[j] <= N; j++)
		{
			vis[i * p[j]] = true;
			if (i % p[j] == 0)
			{
				mu[i * p[j]] = 0;
				break;
			}
			mu[i * p[j]] = mu[i] * mu[p[j]];
		}
	}
	
	for (int i = 1; i <= N; i++)
	{
		sum[i] = sum[i - 1] + mu[i] * mu[i];
	}
}

unordered_map<int, int> dp;

int sublinear(int n)
{
	if (n <= N)
	{
		return sum[n];
	}
	if (dp.count(n))
	{
		return dp[n];
	}
	int res = n, upbound = sqrt(n);
	for (int i = 2; i <= upbound; i++)
	{
		res -= sublinear(n / i / i);
	}
	return dp[n] = res;
}

bool check(int n, int k)
{
	if (sublinear(n) >= k)
	{
		return true;
	}
	return false;
}

int main()
{
	pre();
	int t;
	scanf("%d", &t);
	while (t--)
	{
		int k;
		scanf("%d", &k);
		int l = k, r = 2 * k, ans;
		while (l <= r)
		{
			int mid = ((ll)l + r) >> 1;
			if (check(mid, k))
			{
				r = (ans = mid) - 1;
			}
			else
			{
				l = mid + 1;
			}
		}
		printf("%d\n", ans);
	}
	return 0;
}
上一篇:ansible安装后,默认的root MySQL密码是什么?


下一篇:IO写入数据