标签:
对于一个方程: x^2 + y^2 = z^2,他两两互质的正整数解满足一下条件
1. x = 2*m*n ,y = m^2 - n^2 ,z = m^2 + n^2
2. gcd( m , n) =1, m > n , m 与 n 的奇偶性不同
( 2*m*n )^2 + ( m^2 - n^2 )^2 = ( m^2 + n^2 )显然成立。
下面我们来看一个例题 POJ 1305 传送门
题意:
给定一个n,然后求满足上面那个方程的解的个数以及不满足勾股数的个数。
分析:
题目的数据范围比较小z^2的范围最大为1e6,因此我们可以直接根据奇偶性来
枚举。
代码如下:
#include <iostream> #include <cstdio> #include <cstring> using namespace std; const int maxn = 1e6+10; int gcd(int a,int b){ if(b) return gcd(b,a%b); return a; } int n; bool vis[maxn]; void solve(){ memset(vis,0,sizeof(vis)); int ans1=0,ans2=0; for(int i=1;i*i<=n;i++){ for(int j=2;j*j<=n;j+=2){ if(gcd(i,j)==1){ int l=i,r=j; if(l>r) swap(l,r); if(l*l+r*r<=n){ ans1++; vis[2*r*l]=1; vis[r*r-l*l]=1; vis[l*l+r*r]=1; } int x = 2*l*r; int y = -l*l+r*r; int z = l*l+r*r; for(int k=2;k*z<=n;k++) vis[k*x]=1,vis[k*y]=1,vis[k*z]=1; } } } for(int i=1;i<=n;i++) if(!vis[i]) ans2++; printf("%d %d\n",ans1,ans2); } int main() { while(~scanf("%d",&n)){ solve(); } return 0; }
例题二:HDU3939 Sticks and Right Triangle 传送门
题意:
题意很简单就是求小于n的满足那个方程的解的个数,但是与上题不同的是这题的
数据范围比较大,l<=1e12,如果直接暴力求解的话肯定会超时。
分析:
首先我们先确定一下m,n的大致范围 m<=sqrt(l-1) n <= sqrt(l - m*m) = t。
然后我们还是只能枚举m.(m > n)我们分成以下两种情况来考虑。
1)m为偶数:
如果 m <= t 那么 n的可能选择就是 phi[m] 表示1~m中与m的互质的数的个数。
如果 m > t 那么 我们可以对m素因子分解,然后通过容斥原理计算1~t内与m
互质的数的个数也就是n的可能选择的方案数。
2)m为奇数:
这种情况下我们需要考虑一下我们需要的是奇偶性与m不相同的且与m互质的n的个
数。
如果 m <= t 那么 n的可能选择就是区间1~m/2内与m互质的数的个数。
如果 m > t 那么 n的可能选择就是 区间1~t/2内与m互质的数的个数。
因为1~m/2之内的与m互质的数只要乘一个2就转化到了区间1~m且仍与m互质。
代码如下:
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> using namespace std; typedef long long LL; const int maxn = 1e6+10; int phi[maxn]; int prime[maxn],cnt,num; LL ans ; LL f[100]; bool vis[maxn]; void init(){//筛法求1e6以内的素数和欧拉函数 cnt = 0; memset(vis,0,sizeof(vis)); for(int i=2;i<maxn;i++){ if(!vis[i]){ prime[cnt++]=i; for(int j=i+i;j<maxn;j+=i) vis[j]=1; } } for(int i=0;i<maxn;i++)phi[i]=i; for(int i=2;i<maxn;i+=2) phi[i]>>=1; for(int i=3;i<maxn;i+=2){ if(phi[i]==i){ for(int j=i;j<maxn;j+=i) phi[j]=phi[j]-phi[j]/i; } } } void get_factor(int x){//对x进行素因子分解 num=0; for(int i=0;i<cnt&&prime[i]*prime[i]<=x;i++){ if(x%prime[i]==0){ f[num++]=prime[i]; while(x%prime[i]==0) x=x/prime[i]; } } if(x>1) f[num++]=x; } void dfs(int id,int mul,int tot,int x){//容斥原理求[1,x]内与y互质的数的个数f[]为i的素因子 if(id==num){ if(tot&1) ans = ans - x/mul; else ans = ans + x/mul; return; } dfs(id+1,mul*f[id],tot+1,x); dfs(id+1,mul,tot,x); } int main() { init(); int t; scanf("%d",&t); while(t--){ LL l; scanf("%I64d",&l); int n = sqrt(l+0.5); ans = 0; for(int i=1;i<=n;i++){//枚举m int lim = sqrt(l-(LL)i*i+0.5); if(i&1){ get_factor(i); if(i<=lim) dfs(0,1,0,i>>1); else dfs(0,1,0,lim>>1); } else{ if(i<=lim) ans=ans+phi[i]; else{ get_factor(i); dfs(0,1,0,lim); } } } printf("%I64d\n",ans); } return 0; }
这题的代码其实还可以优化,我们可以在预处理的时候将1e6之内的数都直接素因子分解
然后再后来调用的时候就可以 O(1)的查询了。
标签:
原文地址:http://blog.csdn.net/bigbigship/article/details/46517641