码迷,mamicode.com
首页 > 其他好文 > 详细

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

时间:2020-03-19 21:56:19      阅读:91      评论:0      收藏:0      [点我收藏+]

标签:main   make   杨辉三角   printf   组合   接下来   a*   tmp   mat   

题目要求当\(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;
}

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

标签:main   make   杨辉三角   printf   组合   接下来   a*   tmp   mat   

原文地址:https://www.cnblogs.com/suxxsfe/p/12527254.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!