1.题目描述:点击打开链接
2.解题思路:本题利用枚举+二分解决。问题的关键是选对枚举对象,因为要找C(n,k)=m,如果枚举n的话,一旦m非常大,枚举n就会十分困难。因此枚举对象应为k。根据组合数的性质易知,C(n,n/2)时是最大值,C(n,1)是最小值。由于固定的是k,因此n=2*k时是最小的范围,n=m是最大的范围,这样,即可通过二分法来寻找n。
本题有一个技巧,即在计算组合数时候,不必完整的计算完毕,只要发现中间已经超过了m,说明这个n肯定不是解,返回m+1即表示无解。
3.代码:
#define _CRT_SECURE_NO_WARNINGS #include<iostream> #include<algorithm> #include<string> #include<sstream> #include<set> #include<vector> #include<stack> #include<map> #include<queue> #include<deque> #include<cstdlib> #include<cstdio> #include<cstring> #include<cmath> #include<ctime> #include<functional> using namespace std; #define pll pair<long long,long long> #define M(a,b) make_pair(a,b) using namespace std; priority_queue<pll, vector<pll >, greater<pll > >q; long long m; long long c(int k, long long n) { int i; long long f = 1; for (i = 1; i <= k; i++) { if ((f / i)>(m / (n - i + 1)))//如果发现算到第i步时结果已经超过m了,说明n肯定不是解,返回一个m+1 return m + 1; f *= (n - i + 1); f /= i; } return f; } void chk() { long long l, r, mid, t; int k; for (k = 1; c(k, 2 * k) <= m; k++)//最小的范围是C(k,2*k) { l = k * 2; r = m; while (l <= r) { mid = (l + r) >> 1; t = c(k, mid); if (t == m) { q.push(M(mid, k)); if (mid == k * 2) break; q.push(M(mid, mid - k)); break; } else if (t<m) l = mid + 1; else r = mid - 1; } } } int main() { //freopen("t.txt", "r", stdin); int n, i; scanf("%d", &n); for (i = 1; i <= n; i++) { cin >> m; chk(); if (q.size()) { printf("%d\n", q.size()); int len = q.size(); for (int i = 0; i < len; i++) { printf("(%lld,%lld)%c", q.top().first, q.top().second, i == len - 1 ? '\n' : ' '); q.pop(); } } else puts("0\n"); } return 0; }
时间: 2024-10-19 09:51:47