题目描述:
相似整数三角形的周长和为N(1<=N<=5000000),问这样的三角形组合有多少种(三角形是有序的)
样例:
N为9时,一共有6种。
(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)
要解决这题,我们先给出一个0(1)的时间复杂度求周长为N的整数三角形的个数的算法。
设三角形三边为a1,a2,a3,a1+a2+a3=N,并且a1<=a2<=a3。
a1最小,所以1<=a1<=N/3。算法的思路是将周长为N的三角形按照最小边分类,也就是
也就是要将N/3个整数加起来就是三角形个数的和了,我们算求和公式里面的某一项时,a1是定值,比如a1等于1时三角形的个数,a1等于2时三角形的个数。。。。。
既然a1是定值,我们可以把a1的值先给a2和a3,因为a2和a3都比a1要大,先给a2和a3分配a1的长度,还剩下N-3*a1个长度,我们可以全部给a3,也就是说满足
a3-a2<=N-3*a1。
由三角形的性质,两长边之差小于第三边,也就是a3-a2<a1,就是a3-a2<=a1-1。
现在a3与a2的差要满足两个限制条件了,一个是N-3a1,一个是a1-1。,一个随着递增一个递减,所以选择分段处理,分界点是a1等于N/4的时候。
当1<=a1<=N/4时,这时候满足a1-1<=N-3*a1,所以我们只需要关注a1-1。
对N分为奇数和偶数谈论
当N是奇数时
a1为1,a3与a2的和是偶数,差也是偶数,小于1的偶数的个数是1,所以a1等于1时,三角形个数为1
a1为3,a3与a2的和是偶数,差也是偶数,小于3的偶数的个数是2,所以a1等于3时,三角形个数为2
a1为5,a3与a2的和是偶数,差也是偶数,小于5的偶数的个数是3,所以a1等于5时,三角形个数为3
。。。。。。。。。。。。。。。。。。
当a1是1,3,5,7.........时三角形的个数是1,2,3,4..................
a1为2,a3与a2的和是奇数,差也是奇数,小于2的奇数的个数是1,所以a1等于2时,三角形个数为1
a1为4,a3与a2的和是奇数,差也是奇数,小于4的奇数的个数是2,所以a1等于4时,三角形个数为2
a1为6,a3与a2的和是奇数,差也是奇数,小于6的奇数的个数是3,所以a1等于6时,三角形个数为3
。。。。。。。。。。。。。。。。。。
当a1是2,4,6,8.........时三角形的个数是1,2,3,4....................
两者结合起来就是1,1,2,2,3,3,4,4...............这样的数列知道个数很方便求和的
同理,当N为偶数时,三角形个数的数列是0,1,1,2,2,3,3,..........
解决完前半段,我们来解决后半段
当N/4+1<=a1<=N/3时,此时N-3*a1<=a1-1,所以我们只需要关注N-3*a1。
我们先给a2和a3分配a1的长度,还剩下N-3*a1的长度,现在要满足a2<=a3,我们可以把0,1,2,.....(N-3*a1)/2的长度分为a2,一共是(N-3*a1)/2+1种方案。
在a1从N/4+1增加到N/3的过程中,N-3*a1是公差为3的等差数列,间隔一个看,可以看作是两个公差为6的等差数列,公差为6的等差数列除以2得到的是公差为3的等差数列,所以后半段是由两个公差为3的等差数列组成,这个求和也很好求,所以得到了0(1)时间复杂度求周长为N的整数三角形的个数。代码里面GetNumber函数就是专门来求这个的。
现在我们来看问题,每一种方案都可以提一个基三角形出来,然后所有的三角形都是这个三角形的整数倍数。我们先求N的约数prime[],约数的个数是m
那么方案数
我们已9来举例子,9的约数是1,3,9
周长为1并且三边公约数为1的三角形个数是0,0*2^8=0
周长为3并且三边公约数为1的三角形个数是1,1*2^2=4,对应的方案是(1,1,1),(1,1,1),(1,1,1)
(1,1,1),(2,2,2)
(2,2,2),(1,1,1)
(3,3,3)
周长为9并且三边公约数为1的三角形个数是2,2*2^0=2,对应的方案是(1,4,4)
(2,3,4)
一共加起来是6种,这里要求基三角形的公约数是1的原因是防止计算重复。
那么现在问题来了,周长为L并且三边公约数为1的三角形该怎么算。
周长为L并且三边公约数为1的三角形的个数=周长为L的三角形的个数减去周长为L三边公约数不为1的个数。
周长为L的三角形个数我们上面已经讲了,有0(1)的时间复杂度的算法。
现在求周长为L三边公约数不为1的个数
设三角形三边为a1,a2,a3,最大公约数为c,我们将三边都除以c可以得到一个周长为L/c并且三边公约数为1的三角形。
周长为L并且三边最大公约数为c的三角形的个数与周长为L/c并且公约数为1的三角形的个数是相等的,因为两者可以互相转换。
将c分下类,c只能是L的约数且不能为1,那儿L/c就是L的约数但不包含L。
周长为L并且三边公约数为1的三角形的个数=周长为L的三角形的个数减去周长为L三边最大公约数为L的约数(不包含1)的三角形的个数
所以周长为L并且三边公约数为1的三角形的个数=周长为L的三角形的个数减去周长为L的约数(不包含L)三边公约数为1的三角形的个数。
那么现在问题就好办了,将N的约数从小到大排序,从小到大开始周长为prime[i]公约数不为1的三角形的个数,后面的计算会用到前面的计算的,也就是代码里面GetValue所做的事情。
整道题的时间复杂度不大, 只与N的约数的个数相关。
#include <iostream> #include <cstdio> #include <math.h> #include <stdlib.h> using namespace std; #define mod 1000000007 int a[50]; long long yueshu[1000]; long long value[1000]; int nyueshu; long long GetNumber(long long N)//N可构成的三角形的个数 { if(N<3) { return 0; } long long result=0; long long n=N/4; if(N%2==1) { if(n%2==1) { result=(1+(n-1)/2)*(n-1)/2+(n+1)/2; } else { result=(1+n/2)*n/2; } } else { if(n%2==1) { result=(1+n/2)*(n/2); } else { result=(1+(n-1)/2)*((n-1)/2)+n/2; } } long long temp=N-N/4*3-3; long long k=temp/3+1; if(temp<0) { return result; } long long t1; long long t2; if(temp%2==0) { t1=temp/2; t2=t1-2; } else { t1=temp/2; t2=t1-1; } if(k%2==1) { result=result+(t1+t1-k/2*3)*(k/2+1)/2+(t2+t2-3*(k/2-1))*(k/2)/2+k; } else { result=result+(t1+t1-3*(k/2-1))*(k/2)/2+(t2+t2-3*(k/2-1))*(k/2)/2+k; } return result; } long long MOD(int N) { int i; long long result=1; int n=0; while(N>0) { n++; a[n]=N%2; N/=2; } for(i=n;i>=1;i--) { result=(result*result)%mod; if(a[i]==1) { result=(result*2)%mod; } } return result; } int cmp(const void *arg1,const void* arg2) { long long *t1=(long long*)arg1; long long *t2=(long long*)arg2; if(*t1<*t2) { return -1; } else { return 1; } } void GetYueshu(int N) { nyueshu=0; int n=(int)sqrt((double)N); int i; for(i=1;i<=n;i++) { if(N%i==0) { if(N==i*i) { nyueshu++; yueshu[nyueshu]=i; } else { nyueshu++; yueshu[nyueshu]=i; nyueshu++; yueshu[nyueshu]=N/i; } } } qsort(yueshu+1,nyueshu,sizeof(yueshu[0]),cmp); } void GetValue() { int i; int j; for(i=1;i<=nyueshu;i++) { value[i]=GetNumber(yueshu[i]); } for(i=1;i<=nyueshu;i++) { for(j=1;j<i;j++) { if(yueshu[i]%yueshu[j]==0) { value[i]-=value[j]; } } value[i]=value[i]%mod; } } long long Calculate(int N) { GetYueshu(N); GetValue(); long long result=0; int i; for(i=1;i<=nyueshu;i++) { result=(result+value[i]*MOD(N/yueshu[i]-1))%mod; } return result; } int main() { int N; int t=1; while(cin>>N) { printf("Case %d: %lld\n",t++,Calculate(N)); } return 0; }