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范围内,就好了.
#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; }