题意
(T)组数据,每次询问第(k)个无平方因子的数((1)不算平方因子),(Tleq 50,kleq 10^9)
题解
(k)的范围很大,枚举肯定不行,也没什么奇妙性质,于是可以想到二分。
二分的话我们需要实现一个函数(f(n))表示(n)以内的数中无平方因子的数个数
这十分经典,相当于求(f(n)=sum_{i=1}^nmu^2(i))
解法就是:我们考虑一个质数(p),(p^2)的倍数都不满足要求,也就是说答案得减去(lfloor frac{n}{p^2} floor)。但显然这个会重复,需要容斥。就是说一个数(x=p_1p_2..p_k),答案得加上((-1)^k lfloor frac{n}{x^2} floor)。
然后就会神奇地发现对于一个正整数(i)它的贡献的系数恰好就是(mu(i))!
也就是说答案就是(sum_{i=1}^{sqrt n} mu(i) lfloor frac{n}{i^2} floor) 。预处理(mu)前缀和然后数论分块就好。
分块那里有一点小细节,与常见数论分块不大一样。还有二分的右边界,可以设为(k)的(4)倍,实际上观察样例就可以猜到(2)倍就可以了。
复杂度为大致为(O(Tsqrt klog k))
#include <algorithm>
#include <cstdio>
#include <cmath>
using namespace std;
typedef long long ll;
const int N = 64000;
int mu[N + 5], p[N + 5], cnt;
bool tag[N + 5];
void sieve(int n) {
mu[1] = 1;
for(int i = 2; i <= n; i ++) {
if(!tag[i]) {
p[cnt ++] = i;
mu[i] = -1;
}
for(int j = 0; j < cnt; j ++) {
if(i * p[j] > n) break ;
tag[i * p[j]] = 1;
if(i % p[j] == 0) {
mu[i * p[j]] = 0;
break ;
}
mu[i * p[j]] = - mu[i];
}
}
for(int i = 2; i <= n; i ++) mu[i] += mu[i - 1];
}
ll calc(ll n) {
ll x = (ll) sqrt(n), ans = 0;
for(ll i = 1, j; i * i <= n; i = j + 1) {
j = sqrt(n / (n / (i * i)));
if(j > x) j = x;
ans += (mu[j] - mu[i - 1]) * (n / (i * i));
}
return ans;
}
int main() {
sieve(N);
int t, k; scanf("%d", &t);
while(t --) {
scanf("%d", &k);
ll l = 1, r = 4e9, mid, ans = -1;
while(l <= r) {
mid = (l + r) >> 1;
if(calc(mid) >= k) r = (ans = mid) - 1;
else l = mid + 1;
}
printf("%lld
", ans);
}
return 0;
}