大佬博客
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);
}
}