【CodePlus 2018 4 月赛】组合数问题 2 - 题解
题目链接
做法
发现 $ f_k(h) = C(k, h) $ 是单峰函数,意味着可以像 【NOI2010】超级钢琴 一样用优先队列维护当前每个 $ k $ 最大值。
发现组合数很大,优先级不容易确定。考虑取组合数的对数,再用对数( $ \log (a \times 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\n", ans);
return 0;
}