P4370 [Code+#4] 组合数问题2-题解-有关对数的小技巧
20230927
P4370 [Code+#4] 组合数问题2-sol
Statement
给你两个数 \(n,k\) , 要求对于组合数 \(C_{a}^{b}\) 找到任何 \(k\) 个, 让他们的和最大, 且组合数各不相同, 当且仅当 \(a,b\) 不完全相同时,组合数不同。
Solution
首先,很容易发现 \(C_{n}^{m}\gt c_{n-1}^{m}\),
所以我们一开始可以先把所有的 \(C_{n}^{i}\) 放到一个大根堆里面,
每一次取出堆顶 \(C_{a}^{b}\) 在把 \(C_{a-1}^{b}\) 压入即可。
但是发现 \(n\) 太大了,堆根本就没法比较。
那怎么办呢?
对于组合数,我们可以考虑取对数比较:
\[\log_2\frac{n!}{m!(n-m)!}\\=\log_2n!-\log_2m!-\log_2(n-m)! \\=\sum_{i=1}^n\log_2i-\sum_{i=1}^m\log_2i-\sum_{i=1}^{n-m}\log_2i
\]
于是预处理 \(\log\) 就行了~
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int N=1e6+5;
const ll mod=1e9+7;
ll jc[N],inv[N],ans=0;
double lg[N];
int n,k;
priority_queue<pair<double,pair<int,int> > > q;
ll qpow(ll a,ll b){
ll res=1ll;
while(b){
if(b&1) res=res*a%mod;
a=a*a%mod;
b>>=1;
}
return res;
}
void init(){
jc[0]=1ll;
for(int i=1;i<=n;i++) jc[i]=1ll*jc[i-1]*i%mod;
inv[n]=qpow(jc[n],mod-2);
for(int i=n;i>=1;i--) inv[i-1]=1ll*inv[i]*i%mod;
for(int i=1;i<=n;i++) lg[i]=lg[i-1]+1ll*log(i);
}
double find(int a,int b){
return lg[b]-lg[a]-lg[b-a];
}
ll getans(int a,int b){
return jc[b]*inv[a]%mod*inv[b-a]%mod;
}
int main(){
/*2023.9.27 H_W_Y P4370 [Code+#4] 组合数问题2 堆*/
scanf("%d%d",&n,&k);init();
for(int i=n;i>=1;i--) q.push({find(i,n),{i,n}});
while(k--){
int a=q.top().second.first;
int b=q.top().second.second;
q.pop();
ans=(ans+getans(a,b))%mod;
q.push({find(a,b-1),{a,b-1}});
}
printf("%lld\n",ans);
return 0;
}
Conclusion
对于数字太大无法比较的情况,我们可以考虑用对数去比较(double 类型)