莫比乌斯反演 洛谷P2257

题意

给定N, M,求1<=x<=N, 1<=y<=M且gcd(x, y)为质数的(x, y)有多少对

题解

莫比乌斯反演

还是先上那几个公式

这一种用的比较多 (公式1)

     

另外一种 (公式2)

    

设 f(x)为区间内gcd(x,y)==x的对数

\sum f(d)=F(x)=[n/x]*[m/x]

由公式1知道

f(x)=\sum (u[d/x] * F[d])=\sum (u[d/x] * [N/d] * [M/d])

其中d是x的倍数

则ans=sigma(f[p]) p为素数

设d=i*p

则f[p]可以化简为

\sum(u[i] * [N/ip] * [M/ip])

i\epsilon [1..min(N/p,M/p)]

则ans可以化为 

\sum_{isprime(p))}^{lim}\sum(u[i] * [N/ip] * [M/ip])

然后我们设ip=T

化简得到

\sum_{isprime(p))}^{lim}\sum_{p|T}^{lim}(u[T/p] * [N/T] * [M/T])

一般是先枚举p,然后枚举T,这样时间会超时

我们不妨换一种思考角度,对于每个数T,我们考虑他的素数因子

则可以写成

\sum_{T=1}^{n}\sum_{p|T,isprime(p)}^{T}(u[T/p] * [N/T] * [M/T])

可以看出来在后面的式子中[N/T]*[M/T]就是个系数项,完全可以提到前面去

所以公式可以写成

\sum_{T=1}^{n}([N/T] * [M/T])\sum_{p|T,isprime(p)}^{T}(u[T/p])

后面的那个东西可以用线性筛法解决

具体代码如下

for (int i=0; i<cnt; i++) {
	int p=prime[i];
	for (ll T=p; T<=n; T+=p) F[T]+=mu[T/p];
} 

而前面的那块我们可以通过分块来优化

也就是[N/T]*[M/T]在一段区间内的值是一定的,然后我们只要找出这段区间,设这段区间为[i..j]

则这段区间对答案的贡献为[N/T]*[M/T]*(F[j]-F[i-1])

for(int i=1,j;i<=n;i=j+1)
{
	j=min(n/(n/i),m/(m/i));
	ans+=(F[j]-F[i-1])*(n/i)*(m/i);
}

下面上完整的代码

#include<cstdio>
#include<iostream>
#include<cstring>
#define ll long long 
using namespace std;

const int MAXN=10000010;
bool vis[MAXN];
ll mu[MAXN];
ll prime[MAXN];
ll f[MAXN];
ll F[MAXN];
ll n,m;
int T,i,j;

ll read() {
    ll x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
} // 读入优化函数 


void Prime(int n)  
{  
    int cnt=0;  
    memset(vis,0,sizeof(vis));  
    memset(mu,0,sizeof(mu));
    mu[1]=1;
    for(int i=2;i<n;i++) {  
        if(!vis[i]) {  
            prime[cnt++]=i;  
            mu[i]=-1;
        }  
        for(int j=0;j<cnt&&i*prime[j]<n;j++) {  
            ll k=i*prime[j];  
            vis[k]=1;  
            if(i%prime[j]==0) {  
            	mu[k]= 0;
                break;  
            }  
            else mu[k]=-mu[i];  
        }  
    }  
    
	for (int i=0; i<cnt; i++) {
		int p=prime[i];
		for (ll T=p; T<=n; T+=p) F[T]+=mu[T/p];
	} 
	for (int i=1; i<=n; i++) F[i]+=F[i-1];
}  

int main() {
	Prime(MAXN-5);
	T=read();
	while (T--) {
		n=read(); m=read();
		ll ans=0;
		if (n>m) swap(n,m);
		for(int i=1,j;i<=n;i=j+1)
		{
			j=min(n/(n/i),m/(m/i));
			ans+=(F[j]-F[i-1])*(n/i)*(m/i);
		}
		printf("%lld\n",ans);
	}
}

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值