Description
Solution
是P4369 [Code+#4]组合数问题这道题的加强版……好吧,两道题并没有什么关系。
考虑到 (C_n^m > C_{n - 1}^m),所以我们先把 (C_n^1) 放到一个大根堆中,然后每次取出最大的 (C_x^y),把 (C_{x - 1}^y) 放到堆中。
这样重复 (k) 遍,累加答案即可。
但是,组合数过大,取模之后就没有大小关系了,那该怎么办呢?
其实也很简单,取对数就好了。
这就要用到一些高中数学知识了:
(log_2frac{n!}{m!(n-m)!}=log_2n!-log_2m!-log_2(n-m)!=sum_{i=1}^nlog_2i-sum_{i=1}^mlog_2i-sum_{i=1}^{n-m}log_2i)
所以我们求出前缀积,乘法逆元(也要前缀积),(log)(前缀和)即可。
Code
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <cmath>
#define ll long long
using namespace std;
const ll mod = 1e9 + 7;
const ll N = 1e6 + 10;
ll n, k, ans;
struct node{
ll x, y;
double val;
bool operator < (const node &b) const{
return val < b.val;
}
};
priority_queue<node> q;
ll fac[N], inv[N];
double lg[N];
inline void prework(){
inv[0] = inv[1] = fac[0] = fac[1] = 1;
lg[1] = log(1);
for(ll i = 2; i <= N - 10; i++){
inv[i] = (mod - mod / i) * inv[mod % i] % mod;
fac[i] = fac[i - 1] * i % mod;
lg[i] = lg[i - 1] + log(i);
}
for(ll i = 1; i <= N - 10; i++)
inv[i] = (inv[i - 1] * inv[i]) % mod;
}
signed main(){
prework();
scanf("%lld%lld", &n, &k);
for(ll i = 0; i <= n; i++)
q.push((node){n, i, lg[n] - lg[i] - lg[n - i]});;
for(ll i = 1; i <= k; i++){
node now = q.top();
q.pop();
ans = (ans + fac[now.x] * inv[now.y] % mod * inv[now.x - now.y] % mod) % mod;
q.push((node){now.x - 1, now.y, lg[now.x - 1] - lg[now.y] - lg[now.x - 1 - now.y]});
}
printf("%lld
", ans);
return 0;
}