HDU 5663 (莫比烏斯反演)

分析:

這道題就是一道公式推導題,我再來推一遍公式:

f(x) 函數:

f(x)={1,0,x

ANS=n×mni=1mj=1f(gcd(i,j)) 這個時候只需要維護後面一塊就可以了

TEST=i=1nj=1mf(gcd(i,j))=i=1nj=1md=gcd(i,j)f(d)=d=1min(n,m)f(d)i=1nj=1m(gcd(i,j)=d)=d=1min(n,m)f(d)i=1ndj=1md(gcd(i,j)=1)=d=1min(n,m)f(d)i=1ndj=1mdt|gcd(i,j)μ(t)=d=1min(n,m)f(d)t=1min(nd,md)μ(t)×ndt×mdt=g=1min(n,m)ng×mgd|gμ(d)×f(gd)使g=dt

這個時候就搞定了,後面一塊gd|gμ(d)×f(gd) 維護這個式子和,複雜度是o(nlogn) 但是還可降低:gx2|gμ(x2)×f(gx2)xx2|gμ(d) ,這個時候複雜度就是o(n) (複雜度計算就是平方的倒數和,泰勒展開)。
然後維護gd|gμ(d)×f(gd) 的前綴和,就可以分塊做了,分塊的複雜度o(n)

總的複雜度是o(n+Tn)

代碼:

#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <set>
#include <map>
#include <queue>
#include <vector>
#include <string>

using namespace std;

typedef long long LL;
typedef vector <int>    VI;
typedef pair <int,int>  PII;
#define FOR(i,x,y)  for(int i = x;i < y;++ i)
#define IFOR(i,x,y) for(int i = x;i > y;-- i)
#define pb  push_back
#define mp  make_pair
#define fi  first
#define se  second 

const int maxn = 10000010;

int mu[maxn],prime[maxn];
bool check[maxn];

void Mobius(){
    memset(check,false,sizeof(check));
    prime[0] = 0;
    mu[1] = 1;
    FOR(i,2,maxn){
        if(!check[i])   {mu[i] = -1;prime[++prime[0]] = i;}
        FOR(j,1,prime[0]+1){
            if(i*prime[j] > maxn)  break;
            check[i*prime[j]] = true;
            if(i% prime[j] == 0)    {mu[i*prime[j]] = 0;break;}
            else    {mu[i*prime[j]] = -mu[i];}
        }
    }
}

LL sum[maxn];
int n,m;

LL calc(int x,int y){
    LL ans = 0;
    int i = 1;
    while(i <= x){
        int p = x/i,q = y/i;
        int j = min(x/p,y/q);
        ans += (LL)p*q*(sum[j]-sum[i-1]);
        i = j+1;
    }
    return ans;
}

int main(){
    Mobius();
    memset(sum,0,sizeof(sum));
    FOR(i,1,maxn){
        int k = i*i;
        if(k >= maxn)   break;
        for(int j = k;j < maxn;j += k)  sum[j] += mu[j/k];
    }
    FOR(i,1,maxn)   sum[i] += sum[i-1];
    int T;  scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&m);
        if(n < m)   swap(n,m);
        printf("%I64d\n",(LL)n*m-calc(m,n));
    }
    return 0;
}
發佈了157 篇原創文章 · 獲贊 13 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章