BZOJ2440 莫比烏斯反演 + 二分+ 容斥
阿新 • • 發佈:2019-02-19
第一次做莫比烏斯反演,並不太懂,先記錄一下,
x以內i*i的倍數個數為:n/(i*i);故有 Q(x) = sig(mou[i] * n / (i* i));
根據容斥原理可知 對於√x以內的所有質數 x以內的無平方因子數=無需是任何質數的倍數的數的數量(即x)-是至少一個質數平方倍數的數的數量+是至少兩個質數平方倍數的數的數量-是至少三個質數平方倍數的數的數量...
題目分析:題目要求第k個無平方因子數,我們顯然不可能把答案都求出來再查詢,這個資料範圍首先想到的是二分,對於第1-n的無平方因子數我們可以用容斥定理得到,拿總的個數減去4的倍數(-n/4個),減去9的倍數(-n/9個),但是36既是4的倍數又是9的倍數,被減了兩次,要加回來(+n/36),這樣容斥就出來了,前面的符號正好和數字開根號後對應的莫比烏斯函式相同,這樣問題就簡單了,還有一點要說明的是二分的上界開多大,這個也影響著莫比烏斯函式要開多大,我們不妨假設第k個無平方因子數不會超過2k,具體證明我也不會,但是最小的平方因子是4,也就是說每4個數裡必然有一個是平方因子數,同時因為平方因子越往後越大,可以yy出平均每四個數有不超過兩個平方因子數這個結論,所以第k個無平方因子數不會超過2k,(其實打表也可驗證),所以二分上界取2k+1即可,莫比烏斯函式開sqrt(2e9)差不多5e4的樣子
#include <iostream> #include <bits/stdc++.h> using namespace std; #define MAXN 100100 //#define for(i , l , r) for(int i = l ; i <= r ; i ++ ) #define LL long long #define mem(a) memset(a , 0 , sizeof(a)); int mou[MAXN] , p[MAXN]; bool prime[MAXN]; void mobius() { int pnum = 0; memset(prime , true , sizeof(prime)); mou[1] = 1; // time_t per = clock(); for(int i = 2 ; i < MAXN ; i ++) { if(prime[i]) { mou[i] = -1; p[pnum++] = i; } for(int j = 0 ; j < pnum && i * p[j] < MAXN ; j ++) { prime[i * p[j]] = false; if(i % p[j] == 0) { mou[i*p[j]] = 0; break; } mou[i*p[j]] = -mou[i]; } } // time_t now = clock(); // cout << now - per << endl; } LL solve(LL mid) { LL pos = 0; for(int i = 1 ; i * i <= mid ; i ++) { pos += (LL)mou[i]*(mid/(i*i)); } return pos; } int main() { mobius(); int t ; LL k; scanf("%d" , &t); while(t--) { scanf("%lld" , &k); LL l = 1 , r = 2*k + 1; while(l <= r) { LL mid = (l + r) >> 1; if(solve(mid) < k) l = mid + 1; else r = mid - 1; } printf("%lld\n" , l); } return 0; }