题目描述
二项式定理(英语:Binomial theorem),又称cxdcb二项式定理,由艾萨克•cxdcb于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("%dn",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;}