莫比乌斯反演

大佬博客
hdu 1695
题意:让求从a~b区间和c~d区间有多少对的数的gcd为k,不能有重复的,gcd(2,3)和gcd(3,2)算重复的。
莫比乌斯函数应用的经典题,先求出所有的值然后再减去重复区间的值就好了。

假设F(n)的值为gcd(x,y)==n(a<=x<=b,c<=y<=d)的有多少种。
G(n)代表n|gcd(x,y),(a<=x<=b,c<=y<=d)的有多少种。
由莫比乌斯反演得F(n)=sigma(u(d/n)*G(d)),(n|d),sigma为求和的意思。
由题意可知G(n)==(b/n-(a-1)/n)*(d/n-(c-1)/n)。
求F(k)即可,记得要减去重复的。
这里有有关莫比乌斯函数筛法的求解过程

#include<cstdio>
#include<queue>
#include<cstring>
#include<iostream>
#include<algorithm>
#define LL long long
using namespace std;
const int maxn=1e6+10;
const LL mod=1e9+7;
int p[maxn],mu[maxn];
bool vis[maxn];
void mobius()//求莫比乌斯函数
{
    int cont=0;
    mu[1]=1;
    for(int i=2; i<maxn; i++)
    {
        if(!vis[i])
        {
            mu[i]=-1;
            p[cont++]=i;
        }
        for(int j=0; j<cont&&p[j]*i<maxn; j++)
        {
            vis[p[j]*i]=true;
            if(i%p[j]==0)
            {
                mu[p[j]*i]=0;
                break;
            }
            else
                mu[p[j]*i]=-mu[i];
        }
    }
}
int main()
{
    mobius();
    int ncase,Z=0;
    scanf("%d",&ncase);
    while(ncase--)
    {
        int a,b,c,d,k;
        scanf("%d%d%d%d%d",&a,&b,&c,&d,&k);
        if(k==0)
        {
            printf("Case %d: 0\n",++Z);
            continue;
        }
        LL ans=0;
        int n=(b,d);
        for(int i=k; i<=n; i+=k)//算出所有的情况
            ans+=1ll*mu[i/k]*(b/i-(a-1)/i)*(d/i-(c-1)/i);
        LL ans2=0;
        int l=max(a,c),r=min(b,d);//求出相交区间
        if(l<=r)
            for(int i=k; i<=r; i+=k)//算出相交区间
                ans2+=1ll*mu[i/k]*(r/i-(l-1)/i)*(r/i-(l-1)/i);
        ans-=ans2/2;//因为相交区间的也算的是重复点的所以要除2
        printf("Case %d: %lld\n",++Z,ans);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值