• [Luogu P4370] 【Code+ #1】组合数问题2


    题意:求k个形如(C_a^b)的不同组合数的最大和。限制:(0 leq a leq n),n,k给定。组合数不同是指:a不同或者b不同。

    分析:
    可以发现我们能用一个堆来维护当前还没算进答案的最大的组合数。
    画出杨辉三角形。

    1 0 0 0 0 0 0 0
    1 1 0 0 0 0 0 0
    1 2 1 0 0 0 0 0
    1 3 3 1 0 0 0 0
    1 4 6 4 1 0 0 0
    1 5 10 10 5 1 0 0
    1 6 15 20 15 6 1 0
    1 7 21 35 35 21 7 1

    又可以发现如果(C_i^j)被取出,那么应该往堆里面扔一个(C_{i-1}^j)比较合算。
    那么做法就出来了:用堆来维护当前没被算进答案的最大的组合数。
    但是不幸的是,数据太大导致stl无法进行运算。
    怎么办呢?
    可以给组合数的值取个对数,然后神奇的事发生了:
    (log C_n^m)
    (= log frac{n!}{m!(n-m)!})
    (= log {n!} - log {m!} - log {(n-m)!})
    (= sum_{i=1}^{n}{log i} - sum_{i=1}^{m}{log i} - sum_{i=1}^{n-m}{log i})
    然后惊奇地发现,求一个(log i)的前缀和就可以了。
    初始化的时候,把(C_n^0),...,(C_n^n)扔堆里边。

    代码:

    #include<cstdio>
    #include<queue>
    #include<algorithm>
    #include<cmath>
    #include<cstring>
    using namespace std;
    const long long p=1e9+7;
    double logn[1000010];
    long long quickpow(long long a,long long k)
    {
    	long long ans=1ll;
    	while(k)
    	{
    		if(k&1)ans=(ans*(a%p))%p;
    		a%=p;
    		a=a*a%p;
    		k>>=1;
    	}
    	return ans;
    }
    long long fac[1000010],inv[1000010];
    struct mancity
    {
    	double lg;long long n,m;
    	friend bool operator <(mancity x,mancity y)
    	{
    		return x.lg<y.lg;
    	}
    };
    priority_queue<mancity>q;
    int main()
    {
    	long long x,y,z,i,j,k;
    	long long ans=0ll;
    	scanf("%lld%lld",&x,&y);
    	fac[0]=1ll;
    	for(i=1;i<=x;i++)
    	fac[i]=(fac[i-1]*i)%p;
    	inv[x]=quickpow(fac[x],p-2);
    	for(i=x-1;i>=0;i--)
    	inv[i]=(inv[i+1]*(i+1))%p;
    	logn[0]=0;
    	for(i=1;i<=x;i++)
    	logn[i]=logn[i-1]+log(i);
    	for(i=0;i<=x;i++)
    	{
    		q.push((mancity){logn[x]-logn[x-i]-logn[i],x,i});
    		//printf("%.8lf %lld %lld
    ",logn[x]-logn[x-i]-logn[i],1ll*x,1ll*i);
    	}
    	for(i=1;i<=y;i++)
    	{
    		j=q.top().n;
    		k=q.top().m;
    		q.pop();
    		ans=(ans+1ll*fac[j]*inv[k]%p*inv[j-k]%p)%p;
    		//printf("%lld ",1ll*fac[j]*inv[k]%p*inv[j-k]%p);
    		j--;
    		q.push((mancity){logn[j]-logn[j-k]-logn[k],j,k});
    	}
    	printf("%lld
    ",ans%p);
    	return 0;
    }
    
  • 相关阅读:
    godaddy 亚太机房 更换 美国机房 全过程(图)
    博客园设置访问密码
    GoDaddy Linux主机支持机房的更换
    今天电信宽代终于装上光纤了,升级或安装光纤需购光猫,可以自购。我来扫盲一下
    我来科普一下为毛很多人升级了20M的电信光纤宽带反而感觉速度更卡了
    百度浏览器使用率统计
    hdu 1281
    C#基于SMTP协议和SOCKET通信,实现邮件内容和附件的发送,并可隐藏收件人
    如何处理标注打架
    提高你的Java代码质量吧:谨慎包装类型的比较
  • 原文地址:https://www.cnblogs.com/Rain142857/p/11834614.html
Copyright © 2020-2023  润新知