首页 > 代码库 > HDU1695:GCD(容斥原理+欧拉函数+质因数分解)好题

HDU1695:GCD(容斥原理+欧拉函数+质因数分解)好题

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1695

题目解析:

Given 5 integers: a, b, c, d, k, you‘re to find x in a...b, y in c...d that GCD(x, y) = k.

题目又说a==c==1,所以就是求[1,b]与[1,d]中gcd等于k的个数,因为若gcd(x,y)==z,那么gcd(x/z,y/z)==1,又因为不是z的倍数的肯定不是,所以不是z的倍数的可以直接去掉,所以只要将b和d除以k,然后就转化成了求两个范围中互质的对数了。即求[1,b/k],与[1,d/k]中互质的数目,让b<d,又因为 (x=5, y=7) and (x=7, y=5) are considered to be the same. 

所以先求[1,b/k]中互质的数目,即phi[1]+phi[2]+phi[3].....+phi[b/k](其中phi[i]为i的欧拉函数值),再从区间[b/k+1,d/k]枚举与区间[1,b/k]中互质的数目。其中求与区间[1,b/k]中互质的数目可以通过容斥原理求得与区间[1,b/k]中不互质的数目,相减便可以求得结果。这题折腾了一中午,一直TLE,代码在后面贴了,之后看大神的博客知道了哪里超时的原因,每个数的质因子可以在打表求欧拉函数的时候顺便求出来,一种哈希的思想,这样就不用在枚举的时候每一个数在求一遍他的质因子了,好题!

代码如下:(608ms)

#include <iostream>#include <stdio.h>#include <string.h>#include <algorithm>#include <queue>#include <math.h>using namespace std;typedef __int64 ll;ll sum,phi[100010];int cnt[100010][11],f[100010],a,b,c,d,x;void init(){    memset(f,0,sizeof(f));    for(int i=1; i<=100000; i++)    {        phi[i]=0;        f[i]=0;    }    phi[1]=1;    for(int i=2; i<=100000; i++)    {        if(!phi[i])        {            for(ll  j=i; j<=100000; j+=i)            {                if(!phi[j]) phi[j]=j;                phi[j]=phi[j]/i*(i-1);                cnt[j][f[j]++]=i;//算是哈希吧,很精辟啊,这种写法要学习            }        }        phi[i]+=phi[i-1];    }}ll gcd(ll A,ll B){    return B==0?A:gcd(B,A%B);}void dfs(ll now,ll num,ll lcm,ll &coun,int key){    lcm=cnt[key][now]/gcd(cnt[key][now],lcm)*lcm;    if(num&1)    {        coun+=b/lcm;    }    else    {        coun-=b/lcm;    }    for(ll i=now+1; i<f[key]; i++)        dfs(i,num+1,lcm,coun,key);}int main(){    int T,K=0;    init();    scanf("%d",&T);    while(T--)    {        scanf("%d%d%d%d%d",&a,&b,&c,&d,&x);        if(x == 0 || x > b || x > d)        {            printf("Case %d: 0\n",++K);            continue;        }        b/=x;        d/=x;        sum=0;        if(b>d) swap(b,d);        sum+=phi[b];        for(int i=b+1; i<=d; i++)        {            ll coun=0;            for(int j=0; j<f[i]; j++)            {                dfs(j,1,cnt[i][j],coun,i);            }            sum+=(b-coun);        }        printf("Case %d: %I64d\n",++K,sum);    }    return 0;}

TLE的:(TLE了一中午 3000ms++)

#include <iostream>#include <stdio.h>#include <string.h>#include <algorithm>#include <queue>#include <math.h>using namespace std;typedef __int64 ll;ll a,b,c,d,x,sum,top,cnt[100010],we;//********int phi[100010],su[100010],prime[100010];void init(){    we=0;    prime[we++]=2;    su[0]=su[1]=0;    su[2]=1;    for(int i=3; i<100005; i++)        if(i%2==0) su[i]=0;        else su[i]=1;    double t=sqrt(100005*1.0);    for(ll i=3; i<=t; i++)    {        if(su[i])        {            for(ll j=i*i; j<100005; j=j+i)            {                su[j]=0;            }        }    }    for(ll i=3; i<=100003; i++)    {        if(su[i]) prime[we++]=i;    }    memset(phi,0,sizeof(phi));    phi[1]=1;    for(ll i=2; i<=100003; i++)    {        if(!phi[i])        {            for(ll  j=i; j<=100003; j+=i)            {                if(!phi[j]) phi[j]=j;                phi[j]=phi[j]/i*(i-1);            }        }    }}ll gcd(ll A,ll B){    return B==0?A:gcd(B,A%B);}void dfs(ll now,ll num,ll lcm,ll &coun){    lcm=cnt[now]/gcd(cnt[now],lcm)*lcm;    if(num&1)    {        coun+=b/lcm;        //printf("hsum======%I64d\n",sum);    }    else    {        coun-=b/lcm;    }    for(ll i=now+1; i<top; i++)        dfs(i,num+1,lcm,coun);}void cal(ll key,ll &coun){    top=0;    ll KK=0;    for(ll i=prime[0]; i<=key; i=prime[++KK])    {        if(key%i==0)        {            cnt[top++]=i;            key/=i;            while(key%i==0)                key/=i;        }    }    if(key!=1)    {        cnt[top++]=key;    }    for(ll i=0; i<top; i++)    {        dfs(i,1,cnt[i],coun);    }}int main(){    int T,K=0;    init();    scanf("%d",&T);    while(T--)    {        scanf("%I64d%I64d%I64d%I64d%I64d",&a,&b,&c,&d,&x);        if(x == 0 || x > b || x > d)        {            printf("Case %d: 0\n",++K);            continue;        }        b/=x;        d/=x;        sum=0;        if(b>d) swap(b,d);        //cout<<"b=="<<b<<" "<<"d=="<<d<<endl;        for(int i=1; i<=b; i++)        {            sum+=phi[i];        }        //cout<<"sumsss=="<<sum<<endl;        for(ll i=b+1; i<=d; i++)        {            if(su[i])            {                sum+=b;                continue;            }            ll coun=0;            cal(i,coun);            sum+=(b-coun);        }        printf("Case %d: %I64d\n",++K,sum);    }    return 0;}

 

HDU1695:GCD(容斥原理+欧拉函数+质因数分解)好题