目录
显示
题目链接
https://www.luogu.org/problem/P4370
题解
组合数显然有如下性质:
$$C_x^y>C_{x-1}^y$$
于是可以用堆维护当前最大组合数值:先将 \(C_n^0 \ldots C_n^n\) 全部插入堆中,每次取出最大值 \(C_x^y\) 后,将 \(C_{x-1}^y\) 插入堆中,直到取够 \(k\) 元素后停止。
但组合数显然会爆 long long
,不能直接比较。
可以考虑将组合数取对数后比较。
$$\log C_x^y=\log \frac{x!}{y!(x-y)!}= \sum_{i=1}^{x} \log i-\sum_{i=1}^y \log i-\sum_{i=1}^{x-y} \log i$$
预处理阶乘,阶乘的逆元,对数前缀和即可。
#include <cmath> #include <iostream> #include <queue> #define MOD 1000000007 using namespace std; struct node { int x,y; double v; bool operator<(const node&a)const { return v<a.v; } }; priority_queue<node> q; double sum[1000005]; long long f[1000005],inv[1000005]; long long fpow(long long x,int y) { long long ans=1; while(y) { if(y&1)ans=ans*x%MOD; x=x*x%MOD; y>>=1; } return ans; } double c1(int x,int y) { return sum[x]-sum[y]-sum[x-y]; } long long c2(int x,int y) { return f[x]*inv[y]%MOD*inv[x-y]%MOD; } int main() { int n,k; cin>>n>>k; f[0]=inv[0]=1; for(int i=1;i<=n;i++) f[i]=f[i-1]*i%MOD; inv[n]=fpow(f[n],MOD-2); for(int i=n;i>1;i--) inv[i-1]=inv[i]*i%MOD; for(int i=1;i<=n;i++) sum[i]=sum[i-1]+log(i); for(int i=0;i<=n;i++) q.push({n,i,c1(n,i)}); long long ans=0; for(int i=1;i<=k;i++) { int x=q.top().x,y=q.top().y; q.pop(); ans=(ans+c2(x,y))%MOD; if(x>y)q.push({x-1,y,c1(x-1,y)}); } cout<<ans<<endl; return 0; }