题意:求使得C(n,k)=m的所有的n,k
根据杨辉三角可以看出,当k固定时,C(n,k)是相对于n递增的;当n固定且k<=n/2时,C(n,k)是相对于k递增的,因此可以枚举其中的一个,然后二分另一个。
我的方法是先预处理出2000以内的全部组合数,然后枚举n,二分找到对应的k<=n/2,然后把(n,k)和(n,n-k)加入到set中。
但这样做有一个缺陷,就是当k太小的时候,n可能会很大,数组存不下,因此当k比较小的时候,应该单独枚举k然后二分找到n。
k=1的时候不用算,直接加入即可。
注意溢出的问题。
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 typedef double db; 5 const ll N=2000+10,inf=0x3f3f3f3f3f3f3f3f; 6 const ll up[]= {0,0,44721361,181714,12449,2608}; 7 ll m,C[N][N]; 8 struct D { 9 ll n,k; 10 bool operator<(const D& b)const {return n!=b.n?n<b.n:k<b.k;} 11 }; 12 set<D> ans; 13 ll c(ll n,ll k) { 14 ll ret=1; 15 for(ll i=1; i<=k; ++i)ret=ret*(n-i+1)/i; 16 return ret; 17 } 18 ll bi(ll l,ll r,ll k) { 19 while(l<=r) { 20 ll mid=(l+r)>>1; 21 ll t=c(mid,k); 22 if(t==m)return mid; 23 t<m?l=mid+1:r=mid-1; 24 } 25 return -1; 26 } 27 int main() { 28 C[0][0]=1; 29 for(ll i=1; i<N; ++i) 30 for(ll j=0; j<=i; ++j) 31 C[i][j]=j==0||j==i?1:min(inf,C[i-1][j]+C[i-1][j-1]); 32 ll T; 33 for(scanf("%lld",&T); T--;) { 34 ans.clear(); 35 scanf("%lld",&m); 36 for(ll i=1; i<N; ++i) { 37 ll j=lower_bound(C[i],C[i]+i/2+1,m)-C[i]; 38 if(C[i][j]==m)ans.insert({i,j}),ans.insert({i,i-j}); 39 } 40 ans.insert({m,1}),ans.insert({m,m-1}); 41 for(ll i=2; i<=5; ++i) { 42 ll j=bi(i,up[i],i); 43 if(j!=-1)ans.insert({j,i}),ans.insert({j,j-i}); 44 } 45 printf("%lld\n",ans.size()); 46 ll f=1; 47 for(D x:ans) { 48 f?f--:printf(" "); 49 printf("(%lld,%lld)",x.n,x.k); 50 } 51 puts(""); 52 } 53 return 0; 54 }
UVA - 1649 Binomial coefficients (组合数+二分)
原文:https://www.cnblogs.com/asdfsag/p/11338485.html