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)    收藏  举报

导航