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


     

    题意:

    从 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
    ",ans);
        }
        return 0;
    }

  • 相关阅读:
    variant conversion error for variable:v8
    oracle第二步创建表空间、用户、授权
    Java WEB 乱码解决大全
    跳转的两种方式(转发与重定向)
    jsp页面中 <%%> <%! %>, <%=%> <%-- --%>有什么区别
    Web.xml中Filter过滤器标签几个说明
    SSH面试题(struts2+Spring+hibernate)
    做一个java项目要经过那些正规的步骤
    web.xml 配置中classpath: 与classpath*:的区别
    Web.xml配置详解之context-param
  • 原文地址:https://www.cnblogs.com/nopartyfoucaodong/p/9374020.html
Copyright © 2020-2023  润新知