您的位置:首页 > 其它

[hdu1695] GCD [莫比乌斯反演]

2018-03-01 15:57 239 查看
题面:

传送门

思路:

100000*100000,n方算法T掉,所以必须考虑更好的简化运算的数学方法

注意到要求gcd(x,y)==e,而gcd(x,y)*m=gcd(x*m,y*m)

我们把b和d都除以e,用新的b和d范围来求gcd(x,y)==1,答案再乘上去就好了

具体方法考虑使用莫比乌斯反演:

设函数F(x)表示gcd(x,y)%x==0的种类数

函数f(x)表示gcd(x,y)==x的种类数

显然F(x)==sigma(f(y))(y是x的倍数)

此时F(x)与f(x)满足莫比乌斯反演的第二种情况

又由前文定义得,我们要求的实际上就是f(1)(在新的b和d下),加上一点去重

f(1)=sigma(d==1...min(b/e,d/e))(mu[d]*F(d))

Code:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define ll long long
using namespace std;
bool vis[100010];int mu[100010],pri[100010],cnt=0;
void init(){
int i=1,j,k;
mu[i]=1;
for(i=2;i<=100000;i++){
if(!vis[i]) pri[++cnt]=i,mu[i]=-1;
for(j=1;j<=cnt;j++){
k=i*pri[j];if(k>100000) break;
vis[k]=1;
if(i%pri[j]==0){mu[k]=0;break;}
else mu[k]-=mu[i];
}
}
}
inline int read(){
int re=0,flag=1;char ch=getchar();
while(ch>'9'||ch<'0'){
if(ch=='-') flag=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9') re=(re<<1)+(re<<3)+ch-'0',ch=getchar();
return re*flag;
}
int main(){
int i,a,b,c,d,e,T=read(),cases=0;ll ans,anss;
init();
while(T--){
a=read();b=read();c=read();d=read();e=read();ans=0;anss=0;
if(!e){printf("Case %d: %d\n",++cases,0);continue;}
b/=e;d/=e;
if(b>d) swap(b,d);
for(i=1;i<=b;i++) ans+=(ll)mu[i]*(b/i)*(d/i);
for(i=1;i<=b;i++) anss+=(ll)mu[i]*(b/i)*(b/i);
printf("Case %d: %lld\n",++cases,ans-anss/2);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: