1. 程式人生 > >BZOJ2440 莫比烏斯反演 + 二分+ 容斥

BZOJ2440 莫比烏斯反演 + 二分+ 容斥

第一次做莫比烏斯反演,並不太懂,先記錄一下,

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