现在的位置: 首页 > 综合 > 正文

hdu 3939 Sticks and Right Triangle 勾股数+容斥原理+欧拉函数

2017年11月21日 ⁄ 综合 ⁄ 共 2351字 ⁄ 字号 评论关闭

题意:

给定一个整数L(L<=1e12),计算(x,y,z)组的个数。其中x<y<z,x^2+y^2=z^2,gcd(x,y)==1,gcd(x,z)==1,gcd(y,z)==1。

题解:

在做题之前先要了解下勾股数:勾股数-维基百科

以下的方法可用来找出勾股数。设 m > n 、 m 和 n 均是正整数,
a = m^2 − n^2,
b = 2*m*n,
c = m^2 + n^2

那么a^2+b^2=c^2
若 m 和 n 是互质,而且 m 和 n 其中有一个是偶数,计算出来的 a, b, c 就是素勾股数,即符合题意的勾股数。(若 m 和 n 都是奇数, a, b, c 就会全是偶数,不符合互质。)

实际情况是我们枚举所有的m(1<=m<=sqrt(L)),从而可以通过j=sqrt(L-i*i),确定n的范围为[1,j],i为枚举的m。然后分情况讨论:

i为偶数:

由于i为偶数,而要求i和j互质,所以j必定是奇数,所以就符合了一奇一偶的要求。

1)i<=j,由于m>n,所以符合条件的个数有phi[i]个(phi[i]为欧拉函数,表示小于i且与i互质的个数),

2)i>j,我们分解i得到所有i的质因子ci,假设有三个质因子,由容斥原理得到fun(j)=j-(j/c1+j/c2+j/c3)+(j/(c1*c2)+j/(c1*c3)+j/(c2*c3))-(j/(c1*c2*c3))。

i为奇数:

由于i为奇数,所以根据要求j必须为偶数。

1)i<=j,由于m>n,所以结果是fun[i/2]。why?因为要保证m为偶数,fun(i/2)表示小于等于i/2的数与i互质的个数,我们记为ai,那么2*ai就是小于i的,与i互质的偶数了;那么还有没可能存在更多的个数呢?

我们来反证下:假设存在一个偶数c,gcd(c,i)==1且c/2不在fun(i/2)范围内。那么gcd(c/2,i)!=1,这与gcd(c,i)==1矛盾。所以至多存在fun(i/2)个偶数c,使得gcd(c,i)==1。

2)i>j,同理,结果为fun[j/2]。

其中容斥定理我们可以用dfs解决。

注意:下面的代码我是初始化所有1e6以内的数的质因子,这是以空间换时间;如果想节约空间,可以每次用到一个数的质因子的时候再求。

代码:

#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <queue>
#include <map>
#include <vector>
using namespace std;

#define LL __int64
const int maxn=1e6+10;
int prime[maxn],check[maxn],tot;
int phi[maxn];
int f[maxn][7];
int tt[maxn];
LL ans;
void get_prime_factor(int n)
{
    int i=0,m,k=n;
    tt[n]=0;
    if(!check[n])
    {
        f[n][tt[n]]=n;
        tt[n]++;
        return ;
    }
    while(n!=1)
    {
        if(n%prime[i]==0)
        {
            f[k][tt[k]]=prime[i];
            tt[k]++;
            while(n%prime[i]==0)n=n/prime[i];
            if(!check[n])
            {
                if(n!=1)
                f[k][tt[k]++]=n;
                return ;
            }
        }
        i++;
    }
}
void init()//预处理,找出所有1e7以内的素数,以减少查找1e14范围数的因子的时间
{           //现行筛素数的方法,时间复杂度为O(n)
    memset(check,false,sizeof(check));
    int i,j;
    tot=0;
    for(i=2;i<maxn;i++)
    {
        if(!check[i])prime[tot++]=i;
        for(j=0;j<tot;j++)
        {
            int c=i*prime[j];
            if(c>=maxn)break;
            check[c]=true;
            if(c==0)break;
        }
    }

    //获得所有数的质因子
    for(i=1;i<maxn;i++)
    {
        get_prime_factor(i);
    }
}
void euler_phi()
{
    int i,j,k;
    //欧拉函数,phi[i]表示不超过i的与i互质的整数个数
    for(i=2;i<maxn;i++)phi[i]=0;
    phi[1]=1;
    for(i=2;i<maxn;i++)
        if(!phi[i])
        for(j=i;j<=maxn;j+=i){
            if(!phi[j])phi[j]=j;
            phi[j]=phi[j]/i*(i-1);
        }
}
void dfs(int t,int num,int n,int sum,int k)
{
    if(t==tt[k])
    {
        if(num&1)ans-=n/sum;
        else ans+=n/sum;
        return ;
    }
    dfs(t+1,num,n,sum,k);
    dfs(t+1,num+1,n,sum*f[k][t],k);
}
int main()
{
    init();
    euler_phi();
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int i,j,k,n,m;
        LL l;
        ans=0;
        scanf("%I64d",&l);
        n=(int)sqrt(l+0.5);
        for(i=1;i<=n;i++)
        {
            m=(int)sqrt(l-(LL)i*i+0.5);
            if(i&1)
            {
                if(i<=m)dfs(0,0,i>>1,1,i);
                else dfs(0,0,m>>1,1,i);
            }
            else
            {
                if(i<=m)ans+=phi[i];
                else dfs(0,0,m,1,i);
            }
        }
        printf("%I64d\n",ans);
    }
    return 0;
}

抱歉!评论已关闭.