1. 程式人生 > >[bzoj2440] [中山市選2011]完全平方數

[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;
}