1. 程式人生 > 實用技巧 >bzoj2440 - 完全平方數(莫比烏斯函式,容斥原理)

bzoj2440 - 完全平方數(莫比烏斯函式,容斥原理)

題目

小 X 自幼就很喜歡數。但奇怪的是,他十分討厭完全平方數。他覺得這些
數看起來很令人難受。由此,他也討厭所有是完全平方數的正整數倍的數。然而
這絲毫不影響他對其他數的熱愛。
這天是小X的生日,小 W 想送一個數給他作為生日禮物。當然他不能送一
個小X討厭的數。他列出了所有小X不討厭的數,然後選取了第 K個數送給了
小X。小X很開心地收下了。
然而現在小 W 卻記不起送給小X的是哪個數了。你能幫他一下嗎?

題解

容易想到二分答案,故關鍵在於求區間[1, n]中不含平方因子的數個數,設這個函式為\(f(n)\)

假設[1, n]中含有質數\(p_1,p_2,...p_k\),由容斥原理可得

\[f(n)=n-(\lfloor\frac{n}{p_1^2}\rfloor+\lfloor\frac{n}{p_2^2}\rfloor+...+\lfloor\frac{n}{p_k^2}\rfloor)+(\lfloor\frac{n}{p_1^2p_2^2}\rfloor+\lfloor\frac{n}{p_1^2p_3^2}\rfloor+...+\lfloor\frac{n}{p_1^2p_k^2}\rfloor+...)-(...)+(...)-... \]

因為分母是平方數,我們可以列舉分母的值,列舉複雜度為\(O(\sqrt{n})\)。那麼前面係數怎麼求?容易發現,這個係數就是莫比烏斯函式的值!質因數個數為奇數時減 和 質因數個數為偶數時加 真好對應莫比烏斯函式的\((-1)^k\)

。根號下分母不存在含平方質因子的情況,正好對應莫比烏斯函式為零的情況。故有

\[f(n)=\sum\limits^{i^2\le n}_{i=1}{\mu(i)\lfloor\frac{n}{i^2}\rfloor} \]

也可以寫成

\[\sum\limits^{n}_{i=1}{\mu(i)^2}=\sum\limits^{i^2\le n}_{i=1}{\mu(i)\lfloor\frac{n}{i^2}\rfloor} \]

#include <bits/stdc++.h>

#define endl '\n'
#define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define FILE freopen(".//data_generator//in.txt","r",stdin),freopen("res.txt","w",stdout)
#define FI freopen(".//data_generator//in.txt","r",stdin)
#define FO freopen("res.txt","w",stdout)
#define mp make_pair
#define seteps(N) fixed << setprecision(N) 
typedef long long ll;

using namespace std;
/*-----------------------------------------------------------------*/

ll gcd(ll a, ll b) {return b ? gcd(b, a % b) : a;}
#define INF 0x3f3f3f3f

const int N = 1e5 + 10;
const double eps = 1e-5;

int mu[N];
int prime[N];
bool vis[N];
int cnt;

void init() {
    mu[1] = 1;
    for(int i = 2; i < N; i++) {
        if(!vis[i]) {
            prime[cnt++] = i;
            mu[i] = -1;
        }
        for(int j = 0; j < cnt; j++) {
            int p = prime[j];
            if(i * p > N) break;
            vis[i * p] = 1;
            if(i % p == 0) {
                mu[i * p] = 0;
                break;
            }
            mu[i * p] = -mu[i];
        }
    }
}

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


int main() {
    IOS;
    init();
    int t;
    cin >> t;
    while(t--) {
        int k;
        cin >> k;
        ll l = 1, r = 1800000000;
        while(l <= r) {
            ll mid = (l + r) / 2;
            if(get(mid) >= k) {
                r = mid - 1;
            } else {
                l = mid + 1;
            }
        }
        cout << l << endl;
    }
}