1. 程式人生 > >HDU 4466 Triangle (邊長a+b+c=M的三角形個數->且gcd(a,b,c)=1的三角形個數)

HDU 4466 Triangle (邊長a+b+c=M的三角形個數->且gcd(a,b,c)=1的三角形個數)

Triangle

Time Limit: 10000/5000 MS (Java/Others)
Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 627 Accepted Submission(s): 292

Problem Description

You have a piece of iron wire with length of n unit. Now you decide to cut it into several ordered pieces and fold each piece into a triangle satisfying:
*All triangles are integral.
* All triangles are pairwise similar.
You should count the number of different approaches to form triangles. Two approaches are considered different if either of the following conditions is satisfied:
*They produce different numbers of triangles.
* There exists i that the ith (again, pieces are ordered) triangle in one approaches is not congruent to ith triangle in another plan.
The following information can be helpful in understanding this problem.
* A triangle is integral when all sides are integer.
*Two triangles are congruent when all corresponding sides and interior angles are equal.
* Two triangles are similar if they have the same shape, but can be different sizes.
*For n = 9 you have 6 different approaches to do so, namely
(1, 1, 1) (1, 1, 1) (1, 1, 1)
(1, 1, 1) (2, 2, 2)
(2, 2, 2) (1, 1, 1)
(1, 4, 4)
(2, 3, 4)
(3, 3, 3)
where (a, b, c) represents a triangle with three sides a, b, c.

Input

There are several test cases.
For each test case there is a single line containing one integer n (1 ≤ n ≤ 5 * 106).
Input is terminated by EOF.

Output

For each test case, output one line “Case X: Y” where X is the test case number (starting from 1) and Y is the number of approaches, moduled by 109 + 7.

Sample Input

1
2
3
4
5
6
8
9
10
11
12
15
19
20
100
1000

Sample Output

Case 1: 0
Case 2: 0
Case 3: 1
Case 4: 0
Case 5: 1
Case 6: 2
Case 7: 1
Case 8: 6
Case 9: 3
Case 10: 4
Case 11: 10
Case 12: 25
Case 13: 10
Case 14: 16
Case 15: 525236
Case 16: 523080925

Source

2012 Asia Chengdu Regional Contest

=================================================

題解

  • 求邊長a+b+c=M的三角形個數
  • 且gcd(a,b,c)=1的三角形個數
  • M長度鐵絲圍成若干個相似三角形的方案數

解析:

  • 第一步:由於每個符合題意的集合都是由相似三角形組成,把任意符合題意集合S中的每個三角形的三條邊a,b,c除以gcd(a,b,c)剩下的部分是一樣的。記為w(x)。
    • 若剩下的部分我們叫a’,b’,c’,若M=a+b+c,M’=a’+b’+c’,記k=M/M’,則用M長度的鐵絲最多能做k個周長為M’的相似三角形。
    • 以a’+b’+c’這樣的三角形為基,用長度為M的鐵絲能作出2^(k-1)種不同的集合。
  • 第二步:周長為M且三條邊a,b,c 滿足 a<=b<=c且gcd(a,b,c)=1的三角形的有多少種?記為g(x)。
    • 這裡探討的三角形,就是第一步中邊長為a’,b’,c’的小三角形。
    • 舉個例子,周長M=15的三角形的就能由周長為3、5和15的小三角形基組成。w(15) = g(3) * 2^(5-1) + g(5) * 2^(3-1) + g(15) * 2^(1-1);
  • 第三步:周長為M且三條邊a,b,c 滿足 a<=b<=c的三角形的有多少種?記為f(x)。
    • 舉個例子,求f(24):周長M=24的三角形有3,4,6,8,12,24這些小三角形基構成,那麼g(24) = f(24) - f(12) - f(6) - f(4) - f(3),減完以後就保證gcd(a,b,c)=1的三角形個數g(24)。
  • 第四步:求f(x)。考慮三角形三條邊a,b,c,a<=b<=c。
    • 分b==c和b!=c討論
      • b==c:由於a<=b==c則a最小為1,c最小為ceil(M/3),所以c 最大為floor((M-1)/2),所以這裡一共有(M-1)/2 - M/3 + (M%3?0:1)種
      • b!=c:相當於b <= c-1,就相當於a,b,c-1構成的三角形,這樣的三角形有f(M-1)種,還要在f(M-1)中排除其中形如a+b==c+1的三角形,因為若a+b+c=M-1,那a,b,c+1這樣的三條邊不能構成一個邊長為M的三角形,因為不滿足a+b>c+1。M-1=a+b+c=2c + 1, M = 2C+2, a+b = c+1 = M/2, 這樣的二元組(a,b)有M/2/2對。此時的M必然為偶數,所以M在偶數的情況下要減去這類情況。
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long LL;
const int maxn = 5*1e6 + 10;
const int mod = 1000000007;

int Pow2[maxn];
int f[maxn];

int main()
{
//    freopen("data.in", "r", stdin);

    Pow2[0] = 1;
    for(int i = 1; i < maxn; i++){//計算周長為i的三角形的個數 a<=b<=c
        f[i] = f[i-1] + (i-1)/2 - i/3 + (i%3?0:1);
        if (!(i&1)) f[i] -= i/4;
        if (f[i] >= mod) f[i] -= mod;
        if (f[i] < mod) f[i] += mod;

        Pow2[i] = (Pow2[i-1]<<1)%mod;
    }

    //計算周長為i的三角形的個數 gcd(a,b,c) = 1, a<=b<=c
    //若gcd(a,b,c)>1 則可以找到周長等於j且j|i的三角形
    //那我們就把這種三角形找出來並且排除
    for(int i = 3; i < maxn; i++){
        for(int j = i+i; j < maxn; j+=i){
            f[j] -= f[i];
            if (f[j] < 0) f[j] += mod;
        }
    }

    int n, cas = 1;
    LL ans;
    while(scanf("%d", &n)!=EOF){
        ans = 0;
        for(int i = 1; i * i <= n; i++){
            if(n % i == 0){
                ans = (ans + (LL)f[i]*Pow2[n/i-1])%mod;
                if (i*i != n){
                    ans = (ans + (LL)f[n/i]*Pow2[i-1])%mod;
                }
            }
        }
        printf("Case %d: %I64d\n", cas++, ans);
    }

    return 0;
}

再接再厲。