zoukankan      html  css  js  c++  java
  • HDU 1695 GCD

    GCD

    Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
    Total Submission(s): 7917    Accepted Submission(s): 2943


    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) = 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.
     

    Input
    The 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.
     

    Output
    For 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
    Hint
    For the first sample input, all the 9 pairs of numbers are (1, 1), (1, 2), (1, 3), (1, 4), (1, 5), (2, 3), (2, 5), (3, 4), (3, 5).
     
    数论,主要运用欧拉函数,法里数列和容斥定理。一开始想了很多没有思路,看了网上对这个题目的理解,顿时感觉豁然开朗。

    欧拉函数是指对于正整数n,欧拉函数是少于或等于n的数中与n互质的数的个数。举个例子,euler(9) = 6,因为9有1,2,4,5,7,8六个互质的数。他的计算方法是:euler(x) = x * (1 - 1/p1) * (1 - 1/p2) * ... *(1 - 1/pn),其中p1,p2,p3...pn是指x的所有的素因数,x不等于0,为整数。有euler(1) = 1.举例来说的话,如21,euler(21) = 21 * (1 - 1/3) * (1 - 1/7) = (3 - 1)*(7 - 1)= 12.我们直接举例的话就是1,2,4,5,8,10,11,13,16,17,19,20这12个数与21互质。


    接着介绍一下法里数列,法里数列是0和1之间所有的最简分数的数列。由小到大,从0到1,根据阶数的不同,法里数列可以有以下的规律和长度。

    1阶:0,1(长度为2)

    2阶:0,1/2, 1(长度为3)

    3阶:0, 1/3, 1/2, 2/3, 1 (长度为5)

    4阶:0, 1/4, 1/3, 1/2, 2/3, 3/4, 1 (长度为7)

    5阶:0, 1/5, 1/4, 1/3, 2/5, 1/2, 2/3, 3/5, 3/4, 4/5, 1(长度为11)
    我们可以看出,通过法里数列的长度可以反映当前阶数所对应的互质的个数加上前面的阶数。我们可以得到F(n) = F(n -  1) + euler((n),其中F为法里数列。我们转化一下思维,是不是这就可以代表当n = 5的时候1到5与其互质的个数的总数。

    那么对于这个题,给出两个范围求其中有有几对数的最大公因数是给出的k,我们转换思维,求得x1 = b / k, x2 = d / k,(假设b < d)如果两个数满足于GCD为k的话,就应该有这两个数除以k得到的数是互质的不然不可能k是最大公因数。那么这个问题就转化为了从给出的两堆数中找有多少个互质的数的对数。其中对于在两个范围内都有的数字,可以采取直接从法里数列中得到,但是如果要是处于两个范围中间的那一段,就需要用x1减去所有的素因数和其倍数,但是这里就发现素因数之间也有公倍数,所以这个地方要用到容斥定理。对于容斥定理的解释,就如我们小学时的奥数题一般,假设有三部分人在做语文数学英语三门学科,那么,总人数就是做数学加做语文加做英语的减去同时做数学语文,同时做语文英语和同时做数学英语的人。当然,剪到这里会发现三个人的交集部分被多减了一遍。所以要加上同时做三门功课的人。这就是容斥定理的实例。在这个题上就是减去所有素因数和他们的倍数,然后加上两两的公倍数,减去三三的公倍数,加上四四的公倍数。。。


    代码如下:

    /*************************************************************************
    	> File Name: GCD.cpp
    	> Author: Zhanghaoran
    	> Mail: chilumanxi@xiyoulinux.org
    	> Created Time: Tue 20 Oct 2015 12:35:26 PM CST
     ************************************************************************/
    
    #include <iostream>
    #include <algorithm>
    #include <cstring>
    #include <cstdio>
    #include <cstdlib>
    
    using namespace std;
    int T;
    long long Euler[100010];
    int num[100010];
    int prim[100010][10];    //根据范围算出的素因数二维数组,2*3*5*7*11*13*17*19已经大于给定的数据范围
    void euler(){            //欧拉函数筛法求
        Euler[1] = 1;
        for(int i = 2; i < 100010; i ++){
            if(!Euler[i]){
                for(int j = i; j < 100010; j += i){
                    if(!Euler[j])
                        Euler[j] = j;
                    Euler[j] = Euler[j] / i * (i - 1);
                    prim[j][num[j] ++] = i;
                }
            }
            Euler[i] += Euler[i - 1];        //i的欧拉函数加上之前的数字得到法里数列
        }
    }
    
    long long work(int x, int Max, int temp){
        int i;
        unsigned long long ans = 0;
        for(int i = x; i < num[temp]; i ++)
            ans += Max / prim[temp][i] - work(i + 1, Max / prim[temp][i], temp); //容斥定理
    
        return ans; 
    }
    
    int main(void){
        cin >> T;
        int a, b, c, d, k;
        euler();
        for(int cas = 1; cas <= T; cas ++){
            cin >> a >> b >> c >> d >> k;
            printf("Case %d: ", cas);
            if(k == 0){
                cout << "0" << endl;
                continue;     
            }
    
            if(b > d)
                swap(b, d);
            b /= k;
            d /= k;
            long long ans = Euler[b];
            for(int i = b + 1; i <= d; i ++)
                ans += b - work(0, b, i);
            cout << ans << endl;
        }
        return 0;
    }



  • 相关阅读:
    setInterval和setTimeOut方法—— 定时刷新
    json
    开发者必备的火狐插件
    C#泛型类和集合类的方法
    jQuery几种常用方法
    SQL语句优化技术分析
    索引的优点和缺点
    Repeater使用技巧
    jQuery 表格插件
    利用WebRequest来实现模拟浏览器通过Post方式向服务器提交数据
  • 原文地址:https://www.cnblogs.com/chilumanxi/p/5136080.html
Copyright © 2011-2022 走看看