组合数问题

分析

首先,我们需要找 \(k\) 个最大的组合数,我们发现对于任一组合数,一定有 \(C_n^m\) > \(C_{n-1}^m\) ,那么我们可以先将 \(C_n^i , 0\leq i \leq n\) 全部都加入一个优先队列里,之后每次都将队首取出,然后将它对应的 \(C_{n-1}^{m}\) 加入即可

solution

对于一个组合数来说,它会非常非常大,以至于我们进行取模操作之后无法正确地找出各个数之间正确的大小关系

所以我们可以像乘积最短路一样,对我们要操作的组合数的大小取 \(log\) ,这样最大的组合数也可以用 \(double\) 全部保存,而且根据 \(log\) 函数优美的单调递增的性质,我们可以有效地保存每一个数之间对应的大小关系

code

#include<iostream>
#include<cstdio>
#include<math.h>
#include<cstring>
#include<algorithm>
#include<map>
#include<bitset>
#include<queue>
#include<vector>
#define ll long long

typedef std::pair<ll,ll> pr;
typedef std::pair<double,pr> zhs;
const ll mod=1e9+7; 
const ll maxn=1e6+10;
ll n,k,ans;
ll c[maxn],inv[maxn];
double lg[maxn];
std::priority_queue<zhs> q;

inline ll ksm(ll a,ll b,ll p)
{
	ll ret=1;
	while(b)
	{
		if(b&1) ret=ret*a%p;
		a=a*a%p;
		b>>=1;
	}
	return ret;
}

inline void ny(ll m)
{
	c[1]=1;
	for(int i=2;i<=m;i++)
	{
		c[i]=c[i-1]%mod*i%mod;
	}
	inv[m]=ksm(c[m],mod-2,mod);
	inv[m]=(inv[m]+mod)%mod;
	for(int i=m-1;i>=0;i--)
	{
		inv[i]=(i+1)*inv[i+1]%mod;
		inv[i]=(inv[i]+mod)%mod;
	}
	for(int i=1;i<=m;i++)
	{
		lg[i]=lg[i-1]+log(i);
	}
}

int main(void)
{
	scanf("%lld %lld",&n,&k);
	
	ny(n+2);
	
//	for(int i=1;i<=n;i++)
//	{
//		printf("%lld %lld %.3lf\n",c[i],inv[i],lg[i]);
//	}
//	
	for(int i=0;i<=n;i++)
	{
		q.push(zhs(lg[n]-lg[i]-lg[n-i],pr(n,i)));
	}
	
	for(int i=1;i<=k;i++)
	{
		ll d=q.top().second.first;
		ll m=q.top().second.second;
		
		q.pop();
		
		(ans+=(c[d]*inv[m]%mod*inv[d-m]%mod))%=mod;
		
		d-=1;
		
		q.push(zhs(lg[d]-lg[m]-lg[d-m],pr(d,m)));
	}
	
	printf("%lld\n",ans);
	
	return 0;
}
上一篇:乘法逆元 学习笔记


下一篇:题解 LOJ #2527. 「HAOI2018」染色