HDU1695 GCD 【欧拉函数】【容斥原理】

最近看了很多容斥原理的题目,跟离散上学的不大一样了,有时候根本想不到,有些题目看了题解都不懂,凡是牵扯到位运算的我感觉都不简单。。。

题意:求x∈[1,b]和y∈[1,d]的区间内有多少组gcd(x,y)==k,其中(x,y)(y,x)算是一组

思路:先做一个小变换,b,d都除以k,问题变为求x∈[1,b/k]和y∈[1,d/k]的区间内有多少组gcd(x,y)==1

在此基础上,令b=b/k,d=d/k,假设b<d,对于<b的部分,很明显可以用欧拉函数直接求出

对于[b+1,d]的部分,主要思想就是,首先把每个数据做因子分解,求出与其不互质的数据个数,b减去即可

这里就用到了容斥原理,Goal=可以被一个因子整除的个数-可以被两个因子整除的个数+可以被三个因子整除的个数(-1)^(n+1)(整除n个的个数)

假设x=6,因子为2,3,区间为[1,15],map[1|2]={2,4,6,8,10,12,14,}map[1|3]={3,6,9,12,15} map[2|2,3]={6,12}

Goal=7+5-2=10     所以与6互质的有5个

如果p(p!=1)是x的因子,那么在区间[1,d]中,有d/p个数与x不互质

假设x有num个因子,1除外,根据组合数学的公式C1+C2+……=2^num,枚举各类情况


 GCD

 

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. GCD(x, y) means the greatest common divisor of x and y. Since the number of choices may be very large, you're only required to output the total number of different number pairs. 
Please notice that, (x=5, y=7) and (x=7, y=5) are considered to be the same. 

Yoiu can assume that a = c = 1 in all test cases. 
InputThe input consists of several test cases. The first line of the input is the number of the cases. There are no more than 3,000 cases. 
Each case contains five integers: a, b, c, d, k, 0 < a <= b <= 100,000, 0 < c <= d <= 100,000, 0 <= k <= 100,000, as described above. 
OutputFor each test case, print the number of choices. Use the format in the example. 
Sample Input
2
1 3 1 5 1
1 11014 1 14409 9
Sample Output
Case 1: 9
Case 2: 736427
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<cmath>
#include<algorithm>
using namespace std;
#define ll long long
#define MOD 1000000007
#define N 100010
int a,b,c,d,gg;
int gcd(int a,int b)
{
    if(b==0)
        return a;
    return gcd(b,a%b);
}
int lcm(int a, int b)
{
    return a/gcd(a, b)*b;
}
int prime[N],vis[N];
int phi[N];
void init()
{
    int i;
    for(i=2;i<=100000;i++)
        phi[i]=0;
    phi[1]=1;
    for(i=2;i<=100000;i++)
        if(!phi[i])
       for(int j=i;j<=100000;j+=i)
       {
           if(!phi[j]) phi[j]=j;
           phi[j]=phi[j]/i*(i-1);
       }
}
int k;
void Oula(int n)
{
    k=0;
    for(int i=2;i<n;i++)
    {
        if(!vis[i])
            prime[k++]=i;
        for(int j=0;j<k&&i*prime[j]<n;j++)
        {
            vis[i*prime[j]]=1;
            if(i%prime[j]==0)
            break;
        }
    }
}
int num;
int p[N];
void fun(int kk, int &ans) {
    ans = 1;
    int i;
    int cnt = 0;
    for (i = 0; i < num&& kk > 0; kk >>= 1, i++) {
        if (kk & 1) {
            ans = lcm(ans, p[i]);
            cnt++;
        }
    }
    if (!(cnt & 1)) ans = -ans;
}
int cal(int n)
{
    num=0;
    for(int i=0;i<k&&prime[i]*prime[i]<=n;i++)
    {
         if(n%prime[i]==0)
        {
            p[num++]=prime[i];
            while(n%prime[i]==0)
                n/=prime[i];
        }
    }
    if(n!=1)
        p[num++]=n;
    int sum = 0;
    int tmp;
    for (int i=1;i<(1<<num);i++)
    {
        fun(i,tmp);
        tmp=b/tmp;
        sum+=tmp;
    }
    return b-sum;
}
int main()
{
    init();
    Oula(N);
    int n;
    int t;
    cin>>t;
    for(int cas=1;cas<=t;cas++)
    {
        ll ans=0;
        scanf("%d%d%d%d%d",&a,&b,&c,&d,&gg);
        cout<<"Case "<<cas<<": ";
        if(gg==0)
            cout<<0<<endl;
        else
        {
            b/=gg;
            d/=gg;
            if(b>d)
            {
                int temp=b;
                b=d;
                d=temp;
            }
            for(int i=1;i<=b;i++)
                ans+=phi[i];
            for(int i=b+1;i<=d;i++)
                ans+=cal(i);
                cout<<ans<<endl;
        }
    }
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值