终于做完了这道题,发一下自己的理解,踩踩~~
题意:给你一个长度n的铁丝,将它弯成一个三角形,或者分成m段,每段组成一个三角形,要求这些三角形相似,问有多少种不同的方案数?
题解:
设f[x] 表示 三角形三边之和为x 的三角形的个数 ,a b c分别为三角形的三边,并且满足a<=b<=c;
当b==c时:a=1时c取到最大值为maxx= [(x-1)/2], a=b=c时c取到最小值为minx=ceil(x/3) ,所以总共有maxx-minx+1;
当b!=c 时:(该题的第一个亮点啊)
由于 b<c即b<=c-1 三角形(a,b,c-1)也必为一个合理的三角形,则可以由f[x-1] 推到当前符合条件的三角形的个数
当然还有一种特殊情况需要排除,即 前一个状态里 a+b=c+1 的三角形的个数,推到当前状态可知 a+b=c,a+b+c=x
则 a+b=c=x/2; a 可以从 1 取到 [(x/2)/2], 共有 [(x/2)/2] 种情况;
以上我们可以得到f[x] 的所有结果
怎么样求解题目呢?
长度为n的铁丝,可以分成d 段(n%d==0) ,每段都可以组成一个小的三角形,也可以将其中的几段合并,构成一个大的并且和小三角形相似的三角形,共有 2^(n.d-1)种组合方式,则最终的结果为 sum( f[d] * ( 2^(n/d-1) ) )
到这里还没有结束,应为用我们之前计算出来的f[x] 进行计算,答案会偏大,有重复计算,所以 (题目的第二个亮点啊)
令f [x] 中计算的三角形a,b,c满足gcd(a,b,c)=1;删选过程很简单,如果n%d==0 则 f[n] 中一定包含着f[d] 中所以三角形扩展 n/d 倍后形成的三角形,减去这部分就可以了;
此时这道题就真的完结了!!!
最后提醒一下,注意取模时超int
#include <iostream>
#include<cstdio>
#include<memory.h>
#include<math.h>
#include<algorithm>
using namespace std;
typedef unsigned long long ll;
const unsigned int maxn=5000000+10;
const int MOD=1000000000+7;
unsigned int fac[maxn];
unsigned int f[maxn];
void init()
{
fac[0]=1;
fac[1]=2;
fac[2]=4;
f[3]=1; f[0]=f[1]=f[2]=0;
ll s1,s2;
ll tmp;
for(int x=4;x<=5000000;x++)
{
s1=ceil(x*1.0/3);
s2=(x-1)/2;
tmp=(f[x-1]+s2-s1+1)%MOD;
f[x]=tmp;
if(x%2==0)
{
tmp=(f[x]-(ll)((x/2)/2)+MOD)%MOD;
f[x]=tmp;
}
}
for(int x=3;x<=5000000;x++)
{
fac[x]=(fac[x-1]*2)%MOD;
for(int y=2;x*y<=5000000;y++)
{
tmp=((ll)f[x*y]-f[x]+MOD)%MOD;
f[x*y]=tmp;
}
}
}
int main()
{
//freopen("in.txt","r",stdin);
init();
int n,tcase=0;
while(~scanf("%d",&n))
{
printf("Case %d: ",++tcase);
if(n<3)
{
printf("0\n");
continue;
}
else
{
ll ans=0;
for(int i=1;i*i<=n;i++) if(n%i==0)
{
ans+=((ll)f[i]*fac[n/i-1])%MOD;
ans=ans%MOD;
if(i*i!=n) ans=(ans+((ll)f[n/i]*fac[i-1])%MOD)%MOD;
}
printf("%I64d\n",ans);
}
}
return 0;
}