题解 P4370 【[Code+#4]组合数问题2】
这道题是zhx出的呢~~
题意很简单,从可选元素总数小于n的所有组合数中选出最大的k个。
我们首先面对数据点编程,只选一个的话,最大的绝对是
那么自然而然产生一个问题,其次大的组合数是什么呢?我们无非只有三种选择
这提示了我们,我们可以从
但是这里会出现另一个结论,就是
这是显而易见的。这说明什么呢,如果我的
有了这个结论,我们就可以先把所有
这个时候我们又会遇到一个问题,如何去比较组合数的大小呢,或者说我们堆的排序函数怎么写呢?只需要用到一个简单的技巧。
蒟蒻曾经做过一道zhx的题是取对数比较大小,在这道题里面可以使用。对数函数是有严格单调性的,可以把很大的数字映射到一个小到我们可以比较的值。
对于一个组合数,对数操作就是
我们只需要预处理出来对数函数的前缀和就可以。
完整代码
#include<bits/stdc++.h>
#include<queue>
#define ri register int
#define ll long long
using namespace std;
ll mod=1e9+7;
ll jc[1000100],inv[1000100];
double lg[1000100];
struct Num;
struct Num{
int a,b; // 表示b里面选a个
ll w; // 表示取模后的组合数数值
double l; // 表示组合数的log值
Num(int p,int q) //初始化一个组合数
{
this->a=p;
this->b=q;
this->l=lg[b]-lg[a]-lg[b-a];
this->w=((jc[b]*inv[a])%mod)*inv[b-a]%mod;
}
bool operator < (const Num &r) const //通过比较对数大小来比较组合数大小
{
return l<r.l;
}
};
int n,k;
ll ans;
priority_queue <Num> q;
int main()
{
jc[0]=jc[1]=inv[0]=inv[1]=1; //初始化
for(ri i=2;i<=1000009;i++) inv[i]=(mod-mod/i)*inv[mod%i]%mod;
for(ri i=1;i<=1000009;i++) inv[i]=inv[i-1]*inv[i]%mod;
for(ri i=1;i<=1000009;i++) jc[i]=jc[i-1]*i%mod;
for(ri i=1;i<=1000009;i++) lg[i]=lg[i-1]+log(i);
scanf("%d%d",&n,&k);
for(ri i=0;i<=n;i++)
q.push(Num(i,n));
while(k--) //扩展
{
Num tmp=q.top();
q.pop();
ans=(ans+tmp.w)%mod;
q.push(Num(tmp.a,tmp.b-1));
}
printf("%lld",ans);
}