洛谷 P4370 [Code+#4]组合数问题2
Description
Solution
是P4369 [Code+#4]组合数问题这道题的加强版……好吧,两道题并没有什么关系。
考虑到 \(C_n^m > C_{n - 1}^m\),所以我们先把 \(C_n^1\) 放到一个大根堆中,然后每次取出最大的 \(C_x^y\),把 \(C_{x - 1}^y\) 放到堆中。
这样重复 \(k\) 遍,累加答案即可。
但是,组合数过大,取模之后就没有大小关系了,那该怎么办呢?
其实也很简单,取对数就好了。
这就要用到一些高中数学知识了:
\(\log_2\frac{n!}{m!(n-m)!}=\log_2n!-\log_2m!-\log_2(n-m)!=\sum_{i=1}^n\log_2i-\sum_{i=1}^m\log_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\n", ans);
return 0;
}