hdu 1695 GCD【欧拉函数、容斥原理】
hdu 1695 GCD【欧拉函数、容斥原理】
http://acm.hdu.edu.cn/showproblem.php?pid=1695
【题目大意】
输入a、b、c、d、k,求在a和b之间有数x,c和d之间有数y,x和y的最大公约数是k,问一共有多少组x和y。(a和c都等于1……)
【提交情况】
Tle若干,没找到合适的算法
【解题思路】
将b和d都除以k,要求的就是1到b之间找个x,1到q之间找个y,共有多少对x和y互质。假设b<=d,对于所有小于b的数i,只需要用过欧拉函数算出和i互质的数的数目,并不断加和,得到最终解。对于大于b小于d的数i,则需要利用容斥原理,自己进行计算,其实就是找到小于b与i互质的数的对数。将i分解质因数以后,想找到和i互质的数,只需要去掉不是和i互质的数,也就是含有i的质因子的数,于是先后去掉含i一个质因子的数,去的时候回多去,加上含两个因子的……即是容斥原理。
【AC的代码】
#include <iostream>
using namespace std;
typedef long long i64;
i64 fc[110000],divNum[110000],fcNum,change,b;
i64 eular(i64 n)//欧拉函数
{
i64 ret=1,i;
for (i=2;i*i<=n;i++)
if (n%i==0){
n/=i,ret*=i-1;
while (n%i==0)
n/=i,ret*=i;
}
if (n>1)
ret*=n-1;
return ret;
}
void getChange(i64 btm,i64 now,i64 top)
{//得到容斥原理每次应该加或剪的内容
i64 get=b,i;
if(now==top)//如果现在已经找到了top个应该找到的数,就进行运算
{
for(i=0;i<top;i++) get/=divNum[i];/*get是有多少个数是divNum的倍数*/
change+=get;
}
else for(i=btm;i<fcNum;i++) divNum[now]=fc[i],getChange(i+1,now+1,top);
}//用divNum记录哪些需要除,从枚举的下限开始枚举,枚举一个递归一层
int main()
{
i64 t,pNum,tmp,a,c,d,k,ans,i,tmpi,j,tNum;
cin>>t;
for(tNum=1;tNum<=t;tNum++)
{
ans=0;
cin>>a>>b>>c>>d>>k;
if(k==0)
{
cout<<"Case "<<tNum<<": "<<0<<endl;
continue;
}
if(b>d){tmp=b; b=d; d=tmp;}
b/=k; d/=k;
for(i=1;i<=d;i++)
{
fcNum=0;
if(i<=b) ans+=eular(i);//小于b时只需要调用欧拉函数
else
{//将i分解质因子
tmpi=i; fcNum=0;
for(j=2;j*j<=tmpi;j++)
{
if(tmpi%j==0)
{
fc[fcNum++]=j;
while(tmpi%j==0) tmpi/=j;
}
}
if(tmpi!=1) fc[fcNum++]=tmpi;
ans+=b;//小于b的数的总个数
for(j=0;j<fcNum;j++)
{//利用容斥原理
change=0;
getChange(0,0,j+1);//调用得到每次change
if(j%2) ans+=change;
else ans-=change;
}
}
}
cout<<"Case "<<tNum<<": "<<ans<<endl;
}
}
posted on 2010-03-05 10:26 liugoodness 阅读(149) 评论(0) 收藏 举报