二项式系数(枚举+二分+二项式定理)

题目描述
  二项式定理(英语:Binomial theorem),又称牛顿二项式定理,由艾萨克•牛顿于1664年、1665年期间提出。该定理给出两个数之和的整数次幂诸如 展开为类似 项之和的恒等式。二项式定理可以推广到任意实数次幂,即广义二项式定理。二项式定理可以用以下公式表示:
   
  我们称C(n,k)为二项式系数。

  现在,你要解决得问题是:已知某二项数系数为m,那么有多少个(n,k)满足(C(n,k)=m,按照n升序排列,若n相同,则按k升序排列。


输入

  第一行为整数T,表示数据组数。接下来的T行,每组数据占一行,为一个整数m。

输出

  每组数据输出两行,第一行为满足条件的(n,k)的数,接下来一行为满足条件的(n,k)对,每队用括号括起来,括号之间用一个空格分开。按n升序排列,若n相同,则按k升序排列。

提示

2<=m<=10^15



分析:

这题刚拿到无从下手,一想反正是练习题时间充足,就找了两三个小时的规律,无奈太弱没找出来....

于是只有通过正当途径找。因为没有找到规律,且这种已知结果求组合数的n,k的问题也没有涉及过,所以枚举什么的应该是无法避免了。

但可以睿智地枚举。首先不要枚举n。因为一旦m大了之后,n的枚举范围可能会异常大,不明智。相对来说k就好枚举多了,根据二项式定理的推论,C(n,n/2)最大,C(n,1)最小。此处k固定,那么n=2*k是最小的二分范围,n=m是最大的二分范围。于是,k通过for循环枚举,每枚举一个k就二分找n。

注意枚举k时也可以加一个判断,如果C(2*k,k)比m还大,说明n取最小的时候都已经无解了,就直接退出。

检查n是否正确:直接计算,因为组合数除掉的数始终比乘上的数小,所以一旦中间结果大于m就直接返回m+1表示不成立。

另:最多的有8对...


#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long LL;
int T;
LL M,tot;
struct data{LL N,K;}ans[10];
bool cmp(data a,data b) {return a.N==b.N?a.K<b.K:a.N<b.N;}

LL check(LL n,LL m)
{
	LL a=1;
	for(int i=1;i<=m;i++)
	{
		if(a/i>M/(n-i+1)) return M+1;
		a*=(n-i+1);
		a/=i;
	}
	return a;
}

void solve()
{
	tot=0;
	LL l,r,mid,t;
	for(LL k=1;check(k*2,k)<=M;k++)
	{
		l=2*k,r=M;
		while(r>=l)
		{
			mid=(r+l)/2;
			t=check(mid,k);
			if(t==M)
			{
				ans[++tot]=(data){mid,k};
				if(mid!=k*2) ans[++tot]=(data){mid,mid-k};
				break;
			}
			else if(t>M) r=mid-1;
			else l=mid+1;
		}
	}
}

int main()
{
//	freopen("in.txt","r",stdin);
	scanf("%d",&T);
	while(T--)
	{
		scanf("%lld",&M);
		solve();
		if(tot) sort(ans+1,ans+1+tot,cmp);
		printf("%d\n",tot);
		for(int i=1;i<=tot;i++) 
		{
			printf("(%lld,%lld)",ans[i].N,ans[i].K);
			if(i!=tot) printf(" ");
		}
		if(T) printf("\n");
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/WWWengine/article/details/81058096