hdu-4466-Triangle 数学题

题目链接:

http://acm.hdu.edu.cn/showproblem.php?pid=4466

题目意思:

一根长为N的木棒,长度分成若干个三角形,使得任意两个三角形都相似。对应顺序三角形全部全等的为同一种分法,求总共有多少种分法。

解题思路:

数学题。

先考虑分成一个三角形的情况。

不妨设a<=b<=c;

1、当b=c时,a至少为1,所以c<=(n-1)/2

而a<=b 所以n-2*c<=c =>c>=n/3; 故共有(n-1)/2-(n/3)+(n/3?0:1)种。

2、当b<c时,肯定有b<=c-1 

此时若a+b>c 则a+b>c-1 

如果a,b,c能构成三角形,则a,b,c-1也一定能够构成三角形。

反过来,如果a,b,c-1能够构成三角形,也即a+b>c-1 当a+b!=c时,一定能使a,b,c构成三角形。故可以通过dp[n-1]递推过来,然后减去a+b=c+1的情况。

此时n=2*c+1,c=(n-1)/2  只有当n为奇数的时候才有可能,而a+b=(n-(n-1)/2),a<=b 所以这种情况共有 (n-(n-1)/2)/2种,化简得(n+1)/4


在考虑有多个三角形的情况。

假设N=a*b 则把b作为一个基本三角形,a个1,作为a个部分,中间有a-1个隔板,每个隔板可选可不选,一共有2^(a-1)种情况。

当所有的隔板都不选的话,就是一个大三角形,所以此时要把,刚才求得的一个三角形中三边不互质的数量减掉。而每个约数,对应的该约数能拆分成一个互质三角形的种数的不互质的三角形,乘以倍数就是大的不互质的三角形。


详见代码:

 

#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<stack>
#include<list>
#include<queue>
#define eps 1e-6
#define INF 0x1f1f1f1f
#define PI acos(-1.0)
#define ll __int64
#define lson l,m,(rt<<1)
#define rson m+1,r,(rt<<1)|1
//#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;
#define M 1000000007
#define N (5000000+10)

/*
freopen("data.in","r",stdin);
freopen("data.out","w",stdout);
*/
int dp[N],bin[N]; //dp[i]表示一个三角形的情况,并且是互质的情况

void init()
{
   dp[3]=1;
   for(int i=4;i<N;i++)
   {
      dp[i]=dp[i-1]+(i-1)/2-i/3+(i%3?0:1); //b==c的情况
      if(!(i&1)) //当前一个为奇数
         dp[i]-=i/4; //减去,递推中不满足要求的部分
      dp[i]=dp[i]%M;
      if(dp[i]<0)
         dp[i]+=M;
   }
   bin[1]=1;
   for(int i=2;i<N;i++)
   {
      bin[i]=(bin[i-1]<<1)%M;
      for(int j=2;i*j<N;j++)
      {
         dp[i*j]-=dp[i]; //减去一个三角形中三边不互质的部分,,
         if(dp[i*j]<0)   //也就是对应的约数的dp[i],对于每个互质的小的a,b,c 对应一个j*a,j*b,j*c
            dp[i*j]+=M;
      }
   }
   return ;
}

int main()
{
   int n,ca=0;

   init();
   //printf("%I64d\n",dp[3]);

   while(scanf("%d",&n)!=EOF)
   {
      int ans=0;
      for(int i=1;i*i<=n;i++)
      {
         if(n%i)
            continue;
         ans=(ans+1LL*dp[i]*bin[n/i])%M; //中间乘法可能会溢int
         if(i*i!=n)
            ans=(ans+1LL*dp[n/i]*bin[i])%M;
      }
      printf("Case %d: %I64d\n",++ca,ans);
   }
   return 0;
}


 

 

posted @ 2013-07-24 20:20  xinyuyuanm  阅读(279)  评论(0编辑  收藏  举报