[bzoj2440] [中山市选2011]完全平方数
Description
小 X 自幼就很喜欢数。但奇怪的是,他十分讨厌完全平方数。他觉得这些数看起来很令人难受。由此,他也讨厌所有是完全平方数的正整数倍的数。然而这丝毫不影响他对其他数的热爱。
这天是小X的生日,小 W 想送一个数给他作为生日礼物。当然他不能送一个小X讨厌的数。他列出了所有小X不讨厌的数,然后选取了第 K个数送给了小X。小X很开心地收下了。
然而现在小 W 却记不起送给小X的是哪个数了。你能帮他一下吗?
Input
包含多组测试数据。文件第一行有一个整数 T,表示测试数据的组数。
第2 至第T+1 行每行有一个整数Ki,描述一组数据,含义如题目中所描述。
Output
含T 行,分别对每组数据作出回答。第 i 行输出相应的
第Ki 个不是完全平方数的正整数倍的数。
Sample Input
4
1
13
100
1234567
Sample Output
1
19
163
2030745
Solution
显然满足单调性,可以二分。
然后问题转化为了\(1\)到\(n\)内有多少个满足条件的数。
对于一个满足条件的数,即每个质因子的指数都是1。
对于一个数\(x=\prod_{i=1}^kp_i^{a_i}\cdot s\),其中前面的\(\prod\)表示\(\forall i,a_i\geqslant 2\)的部分,后面是剩下的。
考虑当且仅当\(k=0\)时,这个数才会被计一次,\(k\ne 0\)时,一次都不会记。
可以想到这样一个函数:
\[\sum_{d|n}\mu(d)=[n=1]
\]
于是可以枚举每个完全平方数\(x^2\),剩下的质因子随便填,然后乘上\(\mu(x)\),这样正好可以满足每个满足条件的数被记一次。
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define ONLINE_JUDGE
#ifdef ONLINE_JUDGE
#define getchar() ((p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin)),p1==p2)?EOF:*p1++)
#endif
namespace fast_IO {
char buf[1<<21],*p1=buf,*p2=buf;
template <typename T> inline void read(T &x) {
x=0;T f=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f;
for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';x*=f;
}
template <typename T,typename... Args> inline void read(T& x,Args& ...args) {
read(x),read(args...);
}
char buf2[1<<21],a[80];int p,p3=-1;
inline void flush() {fwrite(buf2,1,p3+1,stdout),p3=-1;}
template <typename T> inline void write(T x) {
if(p3>(1<<20)) flush();
if(x<0) buf2[++p3]='-',x=-x;
do {a[++p]=x%10+48;} while(x/=10);
do {buf2[++p3]=a[p];} while(--p);
buf2[++p3]='\n';
}
template <typename T,typename... Args> inline void write(T x,Args ...args) {
write(x),write(args...);
}
}
using fast_IO :: read;
using fast_IO :: write;
using fast_IO :: flush;
const int maxn = 1e6+10;
int pri[maxn],tot,vis[maxn],mu[maxn];
void sieve() {
mu[1]=1;
for(int i=2;i<maxn;i++) {
if(!vis[i]) pri[++tot]=i,mu[i]=-1;
for(int j=1;j<=tot&&i*pri[j]<maxn;j++) {
vis[i*pri[j]]=1;
if(i%pri[j]==0) break;
mu[i*pri[j]]=-mu[i];
}
}
}
int check(int n) {
int tmp=0;
for(int i=1;i*i<=n;i++) tmp=tmp+mu[i]*n/(i*i);
return tmp;
}
void solve() {
int n,l,r,mid,ans=1;read(n);l=1,r=n*2;
while(l<=r) {
mid=((l+r)>>1);
if(check(mid)>=n) ans=mid,r=mid-1;
else l=mid+1;
}write(ans);
}
signed main() {
sieve();
int t;read(t);while(t--) solve();
flush();
return 0;
}