洛谷 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;
}

End

posted @ 2021-10-07 11:41  xixike  阅读(25)  评论(0编辑  收藏  举报