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;
}
时间: 2024-10-18 18:41:41

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

HDU1695 GCD (欧拉函数+容斥原理)

求(1,b)区间和(1,d)区间里面gcd(x, y) = k的数的对数(1<=x<=b , 1<= y <= d). b和d分别除以k之后的区间里面,只需要求gcd(x, y) = 1就可以了,这样子求出的数的对数不变. 这道题目还要求1-3 和 3-1 这种情况算成一种,因此只需要限制x<y就可以了 只需要枚举x,然后确定另一个区间里面有多少个y就可以了.因此问题转化成为区间(1, d)里面与x互素的数的个数 先求出x的所有质因数,因此(1,d)区间里面是x的质因数倍数的

uva 11317 - GCD+LCM(欧拉函数+log)

题目链接:uva 11317 - GCD+LCM 题目大意:给定n,求出1~n里面两两的最大公约的积GCD和最小公倍数的积LCM,在10100进制下的位数. 解题思路:在n的情况下,对于最大公约数为i的情况又phi[n/i]次.求LCM就用两两乘积除以GCD即可. #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> using namespace std; ty

HDU 1695 GCD(欧拉函数+容斥原理)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1695 题意:x位于区间[a, b],y位于区间[c, d],求满足GCD(x, y) = k的(x, y)有多少组,不考虑顺序. 思路:a = c = 1简化了问题,原问题可以转化为在[1, b/k]和[1, d/k]这两个区间各取一个数,组成的数对是互质的数量,不考虑顺序.我们让d > b,我们枚举区间[1, d/k]的数i作为二元组的第二位,因为不考虑顺序我们考虑第一位的值时,只用考虑小于i的情

hdu 1695 GCD (欧拉函数、容斥原理)

GCD Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 7357    Accepted Submission(s): 2698 Problem Description Given 5 integers: a, b, c, d, k, you're to find x in a...b, y in c...d that GCD(x, y

hdu 1695 GCD【欧拉函数+容斥原理】

GCD Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 6253    Accepted Submission(s): 2291 Problem Description Given 5 integers: a, b, c, d, k, you're to find x in a...b, y in c...d that GCD(x, y

HDU2588:GCD(欧拉函数的应用)

题目链接:传送门 题目需求:Given integers N and M, how many integer X satisfies 1<=X<=N and (X,N)>=M.(2<=N<=1000000000, 1<=M<=N), 题目解析: 求(X,N),不用想要分解N的因子,分解方法如下,我一开始直接分解for(int i=2;i<=n/2;i++),这样的话如果n==10^9,那么直接超时,因为这点失误直接浪费了一中午 的时间,要这么分解for(in

(hdu step 7.2.2)GCD Again(欧拉函数的简单应用——求[1,n)中与n不互质的元素的个数)

题目: GCD Again Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 125 Accepted Submission(s): 84   Problem Description Do you have spent some time to think and try to solve those unsolved problem afte

hdu 4983 Goffi and GCD(欧拉函数)

Problem Description Goffi is doing his math homework and he finds an equality on his text book: gcd(n−a,n)×gcd(n−b,n)=nk. Goffi wants to know the number of (a,b) satisfy the equality, if n and k are given and 1≤a,b≤n. Note: gcd(a,b) means greatest co

BZOJ2818 GCD 【欧拉函数,线性筛】

题目大意: 给一个范围[1,n],从中找出两个数x,y,使得gcd(x,y)为质数,问有多少对(x,y有序) 解法: 不难,欧拉函数练手题,可以定义集合P ={x|x为素数},那么我们枚举gcd(x,y)可能等于的情况,对于任意p∈P可以得到:gcd(k1·p,k2·p) = p,当且仅当gcd(k1,k2) =1;那么我们就只需要枚举所有的k1,k2了.不妨设k1>k2,那么给定k1,k2的个数就是phi(k1),因为有序,所以给phi*2,但是,这样是否漏算了呢?没错,漏算了(1,1),补上