HDU3939Sticks and Right Triangle勾股数,容斥,欧拉筛,积性函数

若(a, b, c) 三者互质(它们的最大公因数是 1),它们就称为素勾股数。
勾股数的构造:
a = m*m-n*n
b = 2m*n
c = m*m+n*n

若 m 和 n 是互质,而且 m 和 n 其中有一个是偶数,计算出来的 (a, b, c) 就是素勾股数。(若 m 和 n 都是奇数, (a, b, c) 就会全是偶数,不符合互质。)
题意:给定L,求不大于L的勾股数(a,b,c)。
设m>n;
则m【1,sqrt(L)] (Lmax = 1e6 )
遍历m , 求得符合条件的n即可.
for : i 1->sqrt(L)
则 Jmax = sqrt(L-i*i) ;
a: if i<=Jmax && (i&1)==0, 易知 n 可取任意小于i互素的数 ,即oula(i) ;

b:else if i<=Jmax && (i&1) == 1 , 则必须n为小于i互素的偶数,考虑把i的奇数去掉,即i/2。则【1,i/2]为原来的偶数集合。即求[1,i/2]与i互素的元素个数。正难则反,容斥。
设i的素因子有p1,p2,..pk,含有该因子的事件为A1,A2,…Ak。ans = i/2 - (!A1 + !A2 +…+!Ak) + … + (-1)^k(A1A2…Ak) .
其中Ai = (i/2)/pi. AaAb…Af =(i/2)/(papb…pf) dfs(i/2)

c:else if i >Jmax&& i&1==0 , 求不大于j且与i互素的个数。同理容斥原理。dfs(Jmax)

d:else if i>Jmax&& i&1 ==1,求不大于j且与i互素的偶数的个数。dfs(Jmax/2).
实现时用欧拉函数的积性函数特点

#include<algorithm>
#include<iostream>
#include <string>
#include <cstring>
#include <fstream>
#include <vector>
#include <cmath>
using namespace std;
typedef long long ll ;
vector<ll> pri ;
const int maxn = 1000000 ;
bool Pri[maxn+10] ;
ll check_pri[maxn+10] ;
ll phi[maxn+10] ;
ll pri_num = 0;
ll Ans ;
/******************
ll oula(ll n){
    ll ans = n ;
    for(ll i = 2 ; i*i <= n ; ++i ){
            if(n==1) break;
            if( !(n%i) ) {
                ans = ans/i*(i-1) ;
                while(!(n%i)){
                    n/=i ;
                }
            }
        }
        if(n!=1) ans = ans/n*(n-1) ;
        return ans ;
}
*******************/
void get_prime(){
    Pri[0] = Pri[1] = false ;
    phi[1] = 1 ;
    for(int i = 2 ;i <= maxn ; ++i) Pri[i] = true ;
    for(int i = 2 ; i <= maxn ; ++i){            // 欧拉筛 , 积性函数的欧拉
        if(Pri[i]) {
            check_pri[pri_num++] = i;
            phi[i] = i-1 ;
        }
        for(int j = 0 ; j < pri_num && i*check_pri[j] <= maxn ; ++j){
            Pri[i*check_pri[j]] = false ;
            if(i%check_pri[j] == 0){
                phi[i*check_pri[j]] = phi[i]*check_pri[j] ;
                break;
            }
            phi[i*check_pri[j]] = phi[i]*phi[check_pri[j]] ;
        }
    }
}
void prime(ll n){
    ll num = 0;
    pri.clear() ;
    if(Pri[n]) {        // 优化
        pri.push_back(n) ;
        return ;
    }
//    ll t= (ll)sqrt(n) ;
    for(int i = 0 ; i< pri_num && n>1 ; ++i){       // 优化
        if(n%check_pri[i]==0){
            pri.push_back(check_pri[i]) ;
                while(n%check_pri[i]==0){
                n/=check_pri[i];
            }
            if(Pri[n]) {
                pri.push_back(n) ;
                return ;            // 优化
            }
        }
    }
    return ;
}

//****************
ll bit( ll n){
    ll ans  = 0 ;
    ll num =   pri.size() ;
    for(ll i = 0 ; i < (1<<num) ; ++i){
            ll tmp = 1;
            ll cnt = 0;
        for(ll j = 0 ; j < num ; ++j){
            if(i&(1<<j))
                tmp *= pri[j] , cnt++ ;
        }
        if(cnt&1) ans -= n/tmp;
        else ans += n/tmp ;
    }
    return ans ;
}
//***************/
/*************
void dfs(ll pos , ll len , ll val , ll n){
    if(pos == pri.size()){
        if(len&1){
            Ans -= n/val ;
        }
        else {
            Ans += n/val ;
        }
        return ;
    }
    dfs(pos+1,len,val,n) ;
    dfs(pos+1,len+1,val*pri[pos],n) ;
}
**************/
int main(){
    ll l ;
    int t ;
    cin >> t ;
    get_prime() ;
    while(t--){
            scanf("%lld",&l) ;
            Ans = 0;
        for(ll i = 1 ; i*i <= l ; ++i){
            ll tmp = i*i ;
            ll j = (ll)(sqrt(l-tmp)) ;
            if(i&1){
                prime(i) ;
                if(i <= j ){
//                    dfs(0,0,1,i>>1) ;
                    Ans+= bit(i>>1) ;
                }
                else {
//                    dfs(0,0,1,j>>1) ;
                    Ans+=bit(j>>1) ;
                }
            }
            else {
                if(i <= j){
                    Ans += phi[i] ;
                }
                else {
                    prime(i) ;
//                    dfs(0,0,1,j) ;
                    Ans += bit(j) ;
                }
            }
        }
        printf("%lld\n",Ans) ;
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值