hdu GCD 【欧拉函数,素因子分解,筛选法,容斥原理】

一道不错的数论题,可惜自己想不出,只能参考大牛们的代码~~

http://acm.hdu.edu.cn/showproblem.php?pid=1695

#include<iostream>
#include<vector>
#include<map>
#include<stack>
#include<algorithm>
#include<queue>
#include<list>
#include<set>
#include<string.h>
#include<stdlib.h>
#include<math.h>
#include<stdio.h>
#include<ctype.h>
#include<iomanip>
using namespace std;

#define LL long long
#define pi acos(-1)
/*(1)题目要求gcd(x, y) = k , 可以转换为gcd(x / k , y / k) = gcd(n, m) = 1。相应地,题目中,x 和y的范围也可以相应地转换为[1, b / k] 和 [1, d / k]。
(2)题目中,x, y是无序的, 所以,我们可以假设b <= d。
(3)在[1, d / k] 中枚举每一个 m ,在[1, min(d / k, m - 1)]中找出和 m 互质的数的个数。
①当 m <= b / k的时候,可以直接根据欧拉函数求出小于等于m且与m互质的数的个数。
②当 b / k <= m <= d / k, 要用容斥原理来求。。。
*/
#define N 100000+10

LL eul[N];//每个数的欧拉函数(包括之前所有的数)
int num[N];//每个数的素因子有多少
int prim[N][50];//记录每个数的素因子

void EulerPrime()
{
    int i,j,k;
    eul[1]=1;
    for(i=2;i<=N;i++)
    {
        if(eul[i]==0)
        {//凡进去的i都是质数
            for(j=i;j<=N;j+=i)
            {
                if(eul[j]==0)
                eul[j]=j;
                eul[j]=eul[j]*(i-1)/i;
                prim[j][num[j]++]=i;
            }//cout<<i<<endl;
        }
        eul[i]+=eul[i-1];
    }
}

LL dfs(int index,int res,int cur)//计算小于等于res的数中,与cur不互质的个数
{
    LL ret=0;
    int i;
    for(i=index;i<num[cur];i++)//容斥原理来求A1并A2并A3.....并Ak的元素的数的个数.(还不是很懂!)
    {
        //printf("%d/%d=%d\n",res,prim[cur][i],res/prim[cur][i]);
        ret+=res/prim[cur][i]-dfs(i+1,res/prim[cur][i],cur);
    }
    return ret;
}



int main()
{
 //   freopen("a.txt","r",stdin);
    int a,b,c,d,k;
    int ca;
    int i,j,t;
    EulerPrime();//预处理
    while(scanf("%d",&ca)!=EOF)
    {
        for(t=1;t<=ca;t++)
        {
            scanf("%d%d%d%d%d",&a,&b,&c,&d,&k);
            if(k==0)
            {
                printf("Case %d: 0\n",t);
                continue;
            }
            b/=k;
            d/=k;
            if(b>d)
            swap(b,d);
            LL ans=eul[b];
            for(i=b+1;i<=d;i++)
            ans+=b-dfs(0,b,i);
            printf("Case %d: %I64d\n",t,ans);
        }
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值