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