bzoj 2820: YY的GCD (反演)

26 篇文章 0 订阅

题目描述

传送门

题目大意:
pmin(n,m)i=1nj=1m[gcd(i,j)=p]
其中p是质数。

题解

这道题还是要用到反演啊
如果 F(n)=n|df(d) ,那么 f(n)=n|dμ(dn)F(d)
f(d)=ni=1mj=1[gcd(i,j)=d] 表示满足 gcd(i,j)=d 1<=i<=n,1<=j<=m (i,j) 的对数
F(d)=d|nf(n) 表示满足 d|gcd(i,j) 1<=i<=n,1<=j<=m (i,j) 的对数
F(d)=ndmd
设n<=m,那么上面的式子就转化成了

pmin(n,m)p|dμ(dp)F(d)

pmin(n,m)d=1nμ(d)F(dp)

然后令T=d*p,改变枚举的顺序
T=1nnTmTp|Tμ(Tp)
其中p是质数
那么如果我们能预处理 p|Tμ(Tp) ,这个问题就能在 O(n+m)
我们可以直接暴力枚举质数p,然后用p去更新所有范围内p的倍数。
貌似n内质数的个数是接近 n/logn ,对于每个素数来说,枚举倍数的均摊复杂度是 O(logn) ,所以暴力预处理的复杂度是接近O(n)的。

代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define N 10000000
#define LL long long 
using namespace std;
int prime[N+3],mu[N+3];
LL f[N+3];
bool pd[N+3];
int n,m,T;
void init()
{
    mu[1]=1;
    for (int i=2;i<=N;i++) {
        if (!pd[i]) {
            prime[++prime[0]]=i;
            mu[i]=-1;
        }
        for (int j=1;j<=prime[0];j++) {
            if (i*prime[j]>N) break;
            pd[i*prime[j]]=1;
            if (i%prime[j]==0) {
                mu[i*prime[j]]=0;
                break;
            }
            mu[i*prime[j]]=-mu[i];
        }
    }
    for (int i=1;i<=prime[0];i++) {
        int t=prime[i];
        for (int j=1;j*t<=N;j++) f[j*t]+=mu[j];
    }
    for (int i=1;i<=N;i++) f[i]=f[i-1]+f[i];
}
int main()
{
    freopen("a.in","r",stdin);
    freopen("my.out","w",stdout);
    scanf("%d",&T);
    init();
    while (T--) {
        scanf("%d%d",&n,&m);
        if (n>m) swap(n,m);
        int j=0; LL ans=0;
        for (int i=1;i<=n;i=j+1) {
            j=min(n/(n/i),m/(m/i));
            ans=ans+(f[j]-f[i-1])*(n/i)*(m/i);
        }
        printf("%lld\n",ans);
    } 
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值