既然选择了远方,便只顾风雨兼行|

H_W_Y

园龄:1年11个月粉丝:28关注:15

P4370 [Code+#4] 组合数问题2-题解-有关对数的小技巧

20230927

P4370 [Code+#4] 组合数问题2-sol

Statement

传送门

给你两个数 n,k , 要求对于组合数 Cab 找到任何 k 个, 让他们的和最大, 且组合数各不相同, 当且仅当 a,b 不完全相同时,组合数不同。

Solution

首先,很容易发现 Cnm>cn1m

所以我们一开始可以先把所有的 Cni 放到一个大根堆里面,

每一次取出堆顶 Cab 在把 Ca1b 压入即可。

但是发现 n 太大了,堆根本就没法比较。

那怎么办呢?

对于组合数,我们可以考虑取对数比较:

log2n!m!(nm)!=log2n!log2m!log2(nm)!=i=1nlog2ii=1mlog2ii=1nmlog2i

于是预处理 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 类型)

本文作者:H_W_Y

本文链接:https://www.cnblogs.com/H-W-Y/p/17734266.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   H_W_Y  阅读(37)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起