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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值