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

组合数学练习题(一)——Chemist

时间:2018-07-26 21:16:43      阅读:292      评论:0      收藏:0      [点我收藏+]

标签:include   ram   print   快速   log   ber   func   for   its   

题意:

从 n 个人中选出不超过 k 个人,再在选出的人中选出一些人成为队员,再在队员中选一名队长,求不同的方案数。答案 mod 8388608。

共有T组询问,每次给你n和k。T ≤ 10^4 k ≤ n ≤ 10^5。

分析:

在n个人中选不超过k个人,即可以选择1,2,3...k个人,对于每种情况需要分别计算答案。(C(m,n)表示在n个人中选择m个数的方案数)设选i个人,那么共有C(i,n)种方案,对于每一种方案,在选择的i个人中再选择j名队员,有C(j,i)种方案,对于选择的j名队员,从中再选择一名队长共有C(1,j)=j种方案。根据乘法原理,在n人中选择i人再选择j名队员再选择1名队长的方案数为C(i,n)C(j,i)j。所以我们枚举i,j,再将所有的答案累加就是最终的方案数。

ans=∑(i:1~k)C(i,n)∑(j:1~i)C(j,i)j

但是这种做法的时间复杂度为O(T*k^2)=O(TLE)。那么我们让n个人中选择i个人的做法不变,考虑后面的做法,原做法是先选队员再选队长,我们可以考虑先选队长,共有C(1,i)=i种方案,然后对于剩下的i-1个人,他们既可以当队员又可以不当队员,每个人有两种可能,共有2^(i-1)种情况,优化后的答案为:

ans=∑(i:1~k)C(i,n)i2^(i-1)

2^(i-1)可以用快速幂计算,优化后的时间复杂度为O(Tklogk)还是会超时。那么怎么办呢???

看题!

要mod的数是偶数是不是很奇怪啊,仔细打量我们可以发现,8388608=2^23。而且我们的答案中也有2^(i-1)这种形式,那么当i-1>=23时就不需要计算了,因为mod完的数都为0,对答案没有贡献。这样我们就把复杂度进一步降到了O(T*min(k,23)),是不是非常小啊

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod=(1<<23),M=1e5+10;
int T,n,k;
ll c[M][26];
void prework()
{
    for(int i=0;i<=M;i++)
     c[i][0]=1;
    for(int i=1;i<=M;i++)
     for(int j=1;j<=25;j++)
      c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod;
}
int main()
{
    prework();
    scanf("%d",&T);
    for(int j=1;j<=T;j++)
    {
        ll ans=0;
        scanf("%d%d",&n,&k);
        for(ll i=1;i<=min(k,24);i++)
        {
            ans+=(i*(1<<(i-1)))%mod*c[n][i];
            ans%=mod;
        }
        printf("%lld\n",ans);
    }
    return 0;
}

组合数学练习题(一)——Chemist

标签:include   ram   print   快速   log   ber   func   for   its   

原文地址:https://www.cnblogs.com/nopartyfoucaodong/p/9374020.html

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