hdu 4466 triangle 三角形统计 数学计数

题意:

  统计长度为n的绳子,截成多个相似三角形,方案数

思路:

  先整体看此题,  设定一个函数 g(x), 表示周长为x的本质三角形(a,b,c),其 gcd( a,b,c ) = 1, 的数量

  则我们可以知道,  对于长度为 n 的铁丝, 能拆成 n/d 个长度为 d的 本质三角形, d个点,d-1个空隙, 因为可以

组合,这样得到的三角形两两相似, 方案数就是  g(d) * r^(n/d-1)  . 大意就是利用最小单位的三角形来拼.

d-1个空隙都有两个选择,取于不取,取则分开,不取则组成成一个大三角形,则最终结果为

    \sum g(d) * r^( n/d-1 )    其中  n%d = 0  

  现在问题就是 如何计算 g(x) . 

  我们再设定一个函数 f(x), 表示周长为 x的不同三角形数量. 其实这里也是用到了 单位长度的概念.

对于一个本质三角形( gcd(a,b,c) = 1 ) ,  对于 本质三角形 L , a+b+c = L,  则将其扩大d倍,就得到另外一个三角形

a*d + b*d + c*d = L*d,   显然扩大的三角形 gcd( a*d, b*d, c*d ) = d;  不是本质三角形.  但是我们可以想到  

f( L*d ) 中包含了 一个 本质三角形 g(L) 通过扩大了d倍.  其实我们就可以得出 f(x) 与 g(x)的关系了.

    f( n )  =  \sum g( d )      其中 n%d = 0

  这个公式看起来很眼熟,  其实可以用 Mobius反演的出  g( n ) = \sum u(d) * f( n/d )  . 但是我们其实也可以不这么做.

其实我们可以通过线性筛法得出 g(n), 具体如下:

  由上面计算公式,以及前面的分析过程.我们知道.  f(n) 是 包含了一个 g(d) 然后扩充了 n/d 倍得到的. 若从 f(n)中减去所有的g(d),

其中 d < n, 且 n%d = 0,  则最后剩下那个就是 g(n)了.  所以我们可以 通过线性来求出 g(n).

  现在问题只剩下 如何求出 f(x)了, 若算出f(x),则这道题也就解决了. 而且这里n=1e6, 最好有个O(N)的算法能够得出.

  再重申下 函数f(x),表示周长为 x的不同三角形(a,b,c)的数量,我们假设( a <= b <= c )

  则我们通过枚举最大周长 x, 假设其最大边为 c.

  则问题可以划分为两类 独立:  1: b = c  2: b != c

  第一种情况:  b = c,  则 周长为x的三角形 (a,c,c) 的方案数为:

    因为 a+c+c = x , 且 a <= c 那么

    c最大取  A=floor(   (x-1)/2  ),  c最小取  B=ceil(  x/3  ), 则  此时三角形种类: A-B+1

  第二种情况: b != c,  则 周长为x的三角形 (a,b,c) 的方案数为:

    因为 a+b+c = x, 且 b <= c-1, a+b > c,  

    这里, 我们转而考虑 , 形式如 ( a, b, c-1 ) 的三角形, 其方案数为 f( x-1 ),

因为 b <= c-1, 又 a+b > c-1, 则当 a+b > c ,则其 就是三角形 (a,b,c)下的不同三角形数量 f(x)了. 

但是这里有个地方不满足, 就是当 a+b == c,的时候, 假设其为 M , 则我们就可以得出:  f( x ) = f( x-1 ) - M , (b!=c).

    问题就是如何计算出这个M, 我们继续考虑.  

      a+b+(c-1) = x-1,

    => a+b+c = x,  又因为  a+b = c. 可以得到

    =>    c+c = x ,    2*c = x, 所以 c = x/2, 因为c为整数,所以我们知道,只有当x为偶数时才会出现这个M.

    又 a + b = c = x/2,  且 a <= b , 此时 a 的取值为 [ 1, floor( (x/2)/2 ) ]

    所以  当 x为偶数时, M =  floor( (x/2)/2 )

        当 x为奇数时, M = 0;

    通过以上分析, 我们 可以 O(N)计算出 f(x), 再通过线性筛法得出 g(x), 然后枚举 n的因子,计算求出最终结果.

    另外, 这里因为结果较大, 又N = 1e6, 直接开 long long 的数组内存过大, 因为对 1e9+7取余, 我们可以使用int数组,

注意保证其在int范围内,就好了.

View Code
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
const int mod = (int)1e9+7;
const int N = (int)5e6+10;
typedef long long LL;

int dp[N], fact[N];

void init(){    
    dp[3] = 1;
    for(int n = 4; n < N; n++){
        dp[n] = dp[n-1] + floor((n-1)/2.) - ceil(n/3.) + 1; //case 1;    
        if( !(n&1) ) dp[n] -= floor( (n/2)/2. );
        if( dp[n] < 0 ) dp[n] = dp[n]+mod;
        if( dp[n] >= mod ) dp[n] -= mod;
    }
    fact[1] = 1; fact[2] = 2;
    for(int i = 3; i < N; i++){
        fact[i] = fact[i-1]*2; if(fact[i]>=mod) fact[i]-=mod;
        for(int j = 2; i*j < N; j++){    
            dp[i*j] -= dp[i];
            if( dp[i*j] < 0 ) dp[i*j] += mod;    
        }    
    }
}
int main(){
    init();
    int n, Case = 1;
    while( scanf("%d",&n) != EOF){
        LL res = 0;    
        for(int i = 1; i*i <= n; i++){
            if( n%i == 0 ){
                res = (res + 1LL*dp[i]*fact[(n/i)]%mod )%mod;    
                if( i*i != n )
                    res = (res + 1LL*dp[n/i]*fact[i]%mod )%mod; 
            }    
        }    
        printf("Case %d: %lld\n", Case++, res );    
    }
    return 0;
}

 

posted @ 2013-04-25 20:27  yefeng1627  阅读(470)  评论(0编辑  收藏  举报

Launch CodeCogs Equation Editor