suxxsfe

一言(ヒトコト)

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

题目要求当\(0\leq a\leq b\leq n\)时,\(k\)\(\tbinom{b}{a}\)的和的最大值

观察杨辉三角形,可以发现,最大的\(\tbinom{b}{a}\),为\(\tbinom{n}{\frac{n}{2}}\)
还可以知道,除了当前最大的组合数以外,可能最大的,是它周围的四个
即如果当前确定的最大组合数为\(\tbinom{n}{m}\),则接下来有可能成为最大的数是:\(\tbinom{n}{m-1},\tbinom{n}{m+1},\tbinom{n-1}{m-1},\tbinom{n-1}{m}\)
可以写一个杨辉三角理解一下
然后可以用一个优先队列维护,每次取出当前的最大值
那么如何比较两个组合数的大小?写高精算出来(bushi
用一个神奇的方法:取\(\log\)
\(\log ab=\log a+\log b,\log\frac{a}{b}=\log a-\log b\)
则可以先预处理出\(\log x!,0\leq x\leq n\),然后再算组合数\(\tbinom{n}{m}=\log n!-\log m!-\log (n-m)!\)
然后在堆中比较\(\log\)的大小
然后我愉快的把堆写炸了
算组合数就是预处理阶乘和阶乘逆元就行了
一定要记得判断当前最大的组合数周围的那四个数是否存在,具体见代码

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<iomanip>
#include<cstring>
#include<map>
#define reg register
#define EN std::puts("")
#define LL long long
inline int read(){
	int x=0,y=1;
	char c=std::getchar();
	while(c<'0'||c>'9'){if(c=='-') y=0;c=std::getchar();}
	while(c>='0'&&c<='9'){x=x*10+(c^48);c=std::getchar();}
	return y?x:-x;
}
int n,k;
const LL mod=1e9+7;
struct data{
	int n,m;
	double Log;
}dui[500006];
int size;
LL fac[1000006],inv[1000006];
double Log[1000006];
std::map<std::pair<int,int>,bool>map;
inline void push(data x){
	map[std::make_pair(x.n,x.m)]=1;
	dui[++size]=x;
	reg int i=size,fa;
	while(i>1){
		fa=i>>1;
		if(dui[fa].Log>dui[i].Log) break;
		std::swap(dui[fa],dui[i]);i=fa;
	}
}
inline data pop(){
	data ret=dui[1];dui[1]=dui[size--];
	reg int i=1,ls,rs;
	while((ls=i<<1)<=size){
		rs=ls|1;
		if(rs<=size&&dui[rs].Log>dui[ls].Log) ls=rs;
		if(dui[i].Log>dui[ls].Log) break;
		std::swap(dui[i],dui[ls]);i=ls;
	}
	return ret;
}
inline LL power(LL a,LL b){
	LL ret=1;
	while(b){
		if(b&1) ret=(ret*a)%mod;
		a=(a*a)%mod;b>>=1;
	}
	return ret;
}
inline void pre(){
	fac[0]=inv[0]=1;
	for(reg int i=1;i<=n;i++) fac[i]=(fac[i-1]*i)%mod;
	inv[n]=power(fac[n],mod-2);
	for(reg int i=n-1;i;i--) inv[i]=(inv[i+1]*(i+1))%mod;
	for(reg int i=1;i<=n;i++) Log[i]=log(i)+Log[i-1];
}
inline double getLog(int nn,int mm){
	return Log[nn]-Log[mm]-Log[nn-mm];
}
int main(){
	n=read();k=read();
	pre();
	push((data){n,n/2,getLog(n,n/2)});
	reg data now;
	reg LL ans=0;
	std::pair<int,int>tmp;
	reg int nn,mm;
	while(k--){
		now=pop();
		nn=now.n;mm=now.m;
		ans+=((fac[nn]*inv[mm])%mod*inv[nn-mm])%mod;
		ans%=mod;
//			std::printf("%d %d %d\n",ans,nn,mm);
		if(mm-1>=0){
			tmp=std::make_pair(nn,mm-1);
			if(!map[tmp])
				push((data){nn,mm-1,getLog(nn,mm-1)});
		}
		if(mm+1<=nn){
			tmp=std::make_pair(nn,mm+1);
			if(!map[tmp])
				push((data){nn,mm+1,getLog(nn,mm+1)});
		}
		if(mm-1>=0&&nn-1>=0){
			tmp=std::make_pair(nn-1,mm-1);
			if(!map[tmp])
				push((data){nn-1,mm-1,getLog(nn-1,mm-1)});
		}
		if(nn-1>=mm){
			tmp=std::make_pair(nn-1,mm);
			if(!map[tmp])
				push((data){nn-1,mm,getLog(nn-1,mm)});
		}
	}
	std::printf("%lld",ans);
	return 0;
}
posted @ 2020-03-19 21:15  suxxsfe  阅读(162)  评论(0编辑  收藏  举报