【CodePlus 2018 4 月赛】组合数问题 2

题目链接

【CodePlus 2018 4 月赛】组合数问题 2

做法

发现 $ f_k(h) = C(k, h) $ 是单峰函数,意味着可以像 【NOI2010】超级钢琴 一样用优先队列维护当前每个 $ k $ 最大值。
发现组合数很大,优先级不容易确定。考虑取组合数的对数,再用对数( $ log (a imes b) = log a + log b $ )进行比较。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const double eps = 1e-15;
const int N = 1000010;
const int mod = 1e9 + 7;
int n, k, ans;
int fac[N], ifac[N];
double lg[N];
set<int> st[N];

struct P {
	int x, y; double w;
	P(int X = 0, int Y = 0) : x(X), y(Y) { w = lg[x] - lg[y] - lg[x - y]; }
	inline bool operator<(const P &yy)const { return w + eps < yy.w; }
}; priority_queue<P> q;

inline int add(const int &x, const int &y) {
	return x + y < mod ? x + y : x + y - mod;
}
inline int mul(const int &x, const int &y) { return (int)((ll)x * y % mod); }
int C(int x, int y) {
	if(x < y || x < 0 || y < 0) return 0;
	return mul(fac[x], mul(ifac[y], ifac[x - y]));
}
int main() {
	scanf("%d%d", &n, &k);
	for(int i = 1; i <= n; i++) lg[i] = log(i) + lg[i - 1];
	fac[0] = fac[1] = ifac[0] = ifac[1] = 1;
	for(int i = 2; i <= n; i++) fac[i] = mul(fac[i - 1], i);
	for(int i = 2; i <= n; i++) ifac[i] = mul(mod - mod / i, ifac[mod % i]);
	for(int i = 2; i <= n; i++) ifac[i] = mul(ifac[i - 1], ifac[i]);
	for(int i = 1; i <= n; i++) st[i].insert(i / 2), q.push(P(i, i / 2));
	for(; k; --k) {
		P u = q.top(); q.pop(), ans = add(ans, C(u.x, u.y));
		if(u.y > 0 && !st[u.x].count(u.y - 1))
			st[u.x].insert(u.y - 1), q.push(P(u.x, u.y - 1));
		if(u.y < u.x && !st[u.x].count(u.y + 1))
			st[u.x].insert(u.y + 1), q.push(P(u.x, u.y + 1));
	}
	printf("%d
", ans);
	return 0;
}
原文地址:https://www.cnblogs.com/daniel14311531/p/10911988.html