本原直角三角形及應用

Happig丶發表於2020-10-19

本原直角三角形

本原直角三角形是指三邊長均為整數且兩兩互素的直角三角形

所有的直角三角形的三邊長一定是某個本原直角三角形的對應倍數

定義一

對於互質的一對奇偶性相異的數 m , n ( m > n > 0 ) m,n(m>n>0) m,n(m>n>0),得到唯一一組本原直角三角形,其三邊長度分別為 a = m 2 − n 2 , b = 2 n m , c = m ∗ m + n ∗ n a=m^2-n^2,b=2nm,c=m*m+n*n a=m2n2,b=2nm,c=mm+nn

定義二

對於任意互質奇數 p , q ( p > q > 0 ) p,q(p>q>0) p,q(p>q>0),得到唯一一組本原直角三角形,其三邊長度分別為 a = ( p 2 − q 2 ) / 2 , b = p q , c = ( p 2 + q 2 ) / 2 a=(p^2-q^2)/2,b=pq,c=(p^2+q^2)/2 a=(p2q2)/2,b=pq,c=(p2+q2)/2

定理

  • 本原直角三角形的的兩條直角邊必須一個是奇數一個是 4 4 4的倍數的偶數,斜邊必為 4 4 4的倍數加一。周長和麵積必為偶數(實際上所有的直角三角形的周長面積也一定為偶數)
  • 本原直角三角形的斜邊必為 4 4 4的倍數加一,但是不是所有的 4 4 4的倍數加一的數能構成本原直角三角形的斜邊,奇數 c c c且為 4 4 4的倍數加一能構成本原直角三角形的充要條件為能分解為一個奇數和一個偶數的平方和,即 c = k 2 + r 2 c=k^2+r^2 c=k2+r2,此時不難發現這就是二元二次不定方程的整數解問題。

應用

給定直角三角形周長 n n n求有多少個

因為所有的直角三角形都一定能找到本原直角三角形,又因為本原直角三角形的周長為 2 m 2 + 2 n m 2m^2+2nm 2m2+2nm,因此只需要列舉 m ∈ [ 2 , n ) m\in[2,\sqrt{n}) m[2,n )。然後記錄每種周長對應不同的本原直角三角形個數,那麼我們只需要對 n n n因數分解,然後累加每種因數對於的本原直角三角形個數。

下面因數分解步驟是先線性篩,然後常數時間質因數分解,最後常數時間搜尋; O ( ( n ) 2 ) = O ( n ) O((\sqrt{n})^2)=O(n) O((n )2)=O(n)時間複雜度預處理 g c d gcd gcd O ( ( n ) 2 ) = O ( n ) O((\sqrt{n})^2)=O(n) O((n )2)=O(n)列舉每對 m , n m,n m,n。總體時間複雜度 O ( n ) O(n) O(n)

int gcd[3510][3510];
int prime[maxn / 2], p[maxn], num[1005], pfac[1005], C[maxn];
vector<int> fac;
int cnt1, cnt2, tot;


void init() {
    for (int i = 1; i <= 3500; i++) {
        for (int j = 1; j <= 3500; j++) {
            if (!gcd[i][j]) {
                for (int k = 1; k * i <= 3500 && k * j <= 3500; k++)
                    gcd[k * i][k * j] = k;
            }
        }
    }
    int up = sqrt(maxn), xx = 0;
    for (int m = 2; m < up; m++) {
        for (int n = 1; n < m; n++) {
            if (gcd[m][n] > 1) continue;
            int sym = (n ^ m) & 1, cc = 2 * m * m + 2 * n * m;
            if (sym && cc < maxn) {
                C[cc]++;
            }
        }
    }
}

void euler() {
    cnt1 = 0;
    for (int i = 1; i < maxn; i++) p[i] = i;
    for (int i = 2; i < maxn; i++) {
        if (p[i] == i) prime[++cnt1] = i;
        for (int j = 1; j <= cnt1 && 1LL * i * prime[j] < maxn; j++) {
            p[i * prime[j]] = prime[j];
            if (i % prime[j] == 0) break;
        }
    }
}

void divide(int n) {
    cnt2 = 0;
    while (n != 1) {
        pfac[++cnt2] = p[n];
        n /= p[n];
    }
    sort(pfac + 1, pfac + 1 + cnt2);
    tot = 1, num[tot] = 1;
    for (int i = 2; i <= cnt2; i++) {
        if (pfac[i] == pfac[i - 1]) {
            num[tot]++;
        } else {
            num[++tot] = 1;
            pfac[tot] = pfac[i];
        }
    }
}


void dfs(int d, ll cur = 1) {
    if (d == tot + 1) {
        fac.push_back(cur);
        return;
    }
    for (int i = 0; i <= num[d]; i++) {
        dfs(d + 1, cur);
        cur *= pfac[d];
    }
}

int solve(int n) {
    //主函式初始化init(), euler();
    divide(n);
    fac.clear();
    dfs(1, 1);
    int ans = 0;
    for (int i = 0; i < fac.size(); i++) {
        ans += C[fac[i]];
    }
    return ans;
}

相關文章