zoukankan      html  css  js  c++  java
  • UVa 106

    http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&category=&problem=42&mosmsg=Submission+received+with+ID+13714821


    题目大意:

    给你一个N,计算出1-N内所有的勾股数,要求a和b和c互质,同时计算p,p表示1-N中不能构成勾股数的数字的个数,其中没有互质的也算,比如6,8,10也算算,如果N是10,那么p就是4。

    由于给的N的范围是1000000以内,所以用普通的枚举的方法肯定会TLE的,所以需要进行优化。


    首先考虑平方数的性质,对于一个奇数(2n+1),(2n+1)2 = 4(n2 + n) + 1,所以 (2n+1)2 = 1 (mod 4).

    对于一个偶数(2n),(2n)2 = 4n2,所以(2n)2 = 0 (mod 4)。

    所以如果一个数要是平方数,那么这个数肯定mod 4余一或者余零。

     

    首先,x,y互质,所以x和y中至少有一个奇数。考虑上面说过的平方数的性质,x和y中只有一个是奇数。证明如下:

      先假设x和y都为奇数,那么x = 2m + 1, y = 2n + 1,所以 x2 + y2 = (2m+1)2 + (2n+1)2 = 4(m2 + n2 + m + n) + 2 = 2 (mod 4),所以明显不是平方数。所以x和y中只能有一个奇数。

    又z和x,y都互质,且x,y中有一个偶数,那么z必定也是奇数。


    欧几里得法则:x = sqrt(mn), y = (m - n)/2, z = (m + n)/2.(其中m、n同奇或者同偶,并且mn是完全平方数)

    证明:x2 = mn , y2 = (m2 + 2mn + n2)/4, z2 = (m2 + 2mn + n2)/4, 易得x2 + y2 = z2,,且按照对m和n的限制,x,y和z均为整数。


    丢番图法则: x = m + sqrt(2mn), y = n + sqrt(2mn), z = m + n + sqrt(2mn).(其中2mn为完全平方数)

    证明:x2 = m2 + 2mn + 2m * sqrt(2mn), y2 = n2 + 2mn + 2n * sqrt(2mn), z2 = m2 + n2 + 4mn + 2(m + n) * sqrt(2mn). 易知 x2 + y2 = z2,且按照对m和n的限制,x,y和z均为整数。

    毕达哥拉斯法则: x = 2n + 1,  y = 2n2 + 2n,  z = 2n+ 2n + 1.(n >= 1)即在一组勾股数中,当最小边为奇数的时候,它的平方正好等于另外两个连续的正整数之和。

    证明:x2 = (2n + 1)2 = 4n2 + 4n + 1 = (2n2 + 2n) + (2n2 + 2n + 1),易知 (2n + 1)2 + (2n2 + 2n)2 = (2n2 + 2n + 1)2


    柏拉图法则:x = 2n, y = n2 - 1, z = n2 + 1.(n>=2)。即在一组勾股数中,当最小边为偶数的时候,它的平方和刚刚好等于两个连续整数之和的两倍。

    证明:(x2)/2 = 2n2 = (n2 - 1) + (n2 + 1), 易知 (2n)2 + (n2 - 1)2 = (n2 + 1)2;


    勾股数法则:x = m2 - n2, y = 2mn, z = m2 + n2.(m > n)

    证明:x2 = m4 - 2m2 * n2 + n4, y2 = 4m2 * n2, z2 =m4 + 2m2 * n2 + n4,易得 x2 + y2 = z2.

     

    顺便提一下勾股数通解公式。取定 x (x >= 3)的值后,如果k能使 y = (x2 - k2)/2k为整数,z = y + k,则x,y,z必是勾股数。

    这里,使上式中的(x2 - k2) / 2k的值恒为整数的k条件是:

      若 x ≥3 且为奇数,在 x标准分解因数(包括1)全排列重组乘积中,取小于x的因数积为k。如x=15, 152 = 1 * 32 * 52, k = 1, 3, 5, 32.

      若 x ≥4 且为偶数,在 x2 准分解因数(包括1)中去掉一个2后为有效因数,在有效因数全排列重组乘积中,取小于x偶数因数积为k。 如x=10, 102 = 1 * 22 * 52, k = 2.

    勾股数再生公式。a2 + b2 = c2,那么 x = 2(a + c) + b, y = 2(b + c) + a, z = 2(a + b) + 3c, 那么x2 + y2 = z2.

    上面的各种法则中,明显是勾股数法则最合适,而且还可以将N从10^6降到10^3,这样想怎样暴力都没问题了。不过勾股数法则并不能表示所有的勾股数对,例如9, 12, 15 就没办法找出对应的 m 和 n,但是幸好这个特例是不是互质的,那我们是不是可以勾股数法则可以求出所有的质勾股数,勾股数法会遗漏掉的是m,n为分数的情况和m,n为无理数的情况,接下来分别讨论这两种情况。

      ①假设m,n是分数,分别取m/a和n/b (a,b,m,n均为整数, m/a和n/b都是最简分数,且m/a > n/b),所以x = (m/a)2 - (n/b)2, y = 2mn/ab,z =(m/a)2 + (n/b)2,所以 x + y = 2(m/a)2,因为x和y都是整数,又a2 != 2,所以2(m/a)2显然不是整数,所以矛盾了,该假设不成立。

      ②假设m和n是无理数。分别取m * sqrt(a), n * sqrt(b) ( a,b,m,n均为整数, a和b均无法提取出整数 ,且m * sqrt(a) > n * sqrt(b) )。x = m2 * a - n2 * b, y = 2mnab, z = m2 * a + n2 * b, 因为y是整数,所以a = b,不然y就不是有理数了。所以x = a * (m2 - n2), y = a * 2mna, z = a * (m2 + n2).显然x,y,z不是互质的。

    综上所述,勾股数法则可以求出所有的质勾股数。同时,当我们将质勾股数乘以k倍的时候,可以得到当m和n是无理数的时候遗漏所有勾股数。

    做到这里程序基本就可以过了,但关于m和n的取值还可以进一步优化。当m和n奇偶性相同的时候,所以可以不用考虑。

    令gcd(m,n) = d,当d != 1时,x = m2 - n2可以整除d2, y = 2mn可以整除d2,所以gcd(x,y) = d2,所以也可以不用考虑。


    接下来是代码

    #include<cstring>
    #include<iostream>
    using namespace std;
    const int MAXN = 1000 + 5;
    
    int cnt;
    int N, p;
    int a, b, c;
    int vis[MAXN * MAXN];
    
    int gcd(int x, int y)
    {
        return y == 0 ? x : gcd(y, x % y);
    }
    
    int main()
    {
        cin.sync_with_stdio(false);
    
        while(cin >> N)
        {
            cnt = p = 0;
            memset(vis, 0, sizeof(vis));
    
            for(int n = 1; n * n <= N; ++ n)
                for(int m = n + 1; (c = n * n + m * m) && c <= N; ++ m)
    
                    //m和n同奇偶,那么a,b,c都会是偶数
                    if((m & 1) + (n & 1) == 1 && gcd(m, n) == 1)
                    {
                        ++cnt;
                        b = (m * n) << 1;
                        a = m * m - n * n;
    
                        for(int k = 1; c * k <= N; ++k)
                            vis[k * a] = vis[k * b] = vis[k * c] = 1;
                    }
    
            for(int i = 1; i <= N; ++ i)
                if(vis[i] == 0)
                    ++p;
    
            cout << cnt << ' ' << p << endl;
        }
    
        return 0;
    }
    View Code

     

  • 相关阅读:
    python面试的100题(2)
    面试题目和地址
    python面试的100题(1)
    no module named系列问题解决
    ubuntu16.04无法打开终端
    Reinforcement Learning,微信公众号:DRL学习
    Java中的I/O操作File
    Java中的Date时间转换【SimpleDateFormat (parse和format)】和Calendar日历表
    重写equals方法
    Java中栈,堆,常量池的简单理解
  • 原文地址:https://www.cnblogs.com/tank39/p/3911405.html
Copyright © 2011-2022 走看看