1 条题解

  • 3
    @ 2017-11-08 17:53:17

    友情提示:公式小看不清的话,可以将网页放大!

    比较简单的组合数问题,不会组合数取余的可以看一下这篇文章:
    http://www.cnblogs.com/linyujun/p/5194189.html

    我们可以先将序列进行排序,那么对于第\(i\)个数,他被做为最小数的方案有\(\binom{n-i}{k-1}\)
    即从剩下的比它的数选\(k-1\)个数的方案数。

    需要用到的公式\(\binom{n}{m}=\frac{n!}{m!(n-m)!}\)
    我们可以先预处理出阶乘,以及其逆元,然后再套用上面公式。


    怎样求逆元呢?
    我们可以先用费马小定理求\(n!\)的逆元。
    然后就可以得到
    \(inv[i-1] = inv[i] \times i\)其中\(inv[i]\)代表 \(i !\) 的逆元。

    为什么?

    结合逆元的定义就可以知道是这样子的了


    具体实现参照下面代码

    #include<bits/stdc++.h>
    using namespace std;
    
    const int maxn = 200000+10,p=1e9+7;
    
    int n,k,inv[maxn],fac[maxn],c[maxn];
    
    long long ksm(int a,int b)
    {
        long long ret = 1;
        while(b)
        {
            if(b&1) ret = 1ll*ret*a%p;
            a = 1ll*a*a%p;
            b >>= 1;
        }
        return ret;
    }
    
    void init()
    {
        fac[0]=1;
        for(int i=1;i<=n;i++)//预处理阶乘
            fac[i] = 1ll*fac[i-1]*i%p;
        inv[n] = ksm(fac[n],p-2);//费马小定理求逆元
        for(int i=n-1;i>=0;i--)//预处理阶乘的逆元
            inv[i] = 1ll*inv[i+1]*(i+1)%p;
    }
    
    inline long long comb(int a,int b)
    {
        if(b>a) return 0;
        return 1ll*fac[a]*inv[b]%p*inv[a-b]%p;//用组合数的公式
    }
    
    int main()
    {
        long long ans = 0;
        scanf("%d%d",&n,&k);
        init();
        for(int i=1;i<=n;i++)
            scanf("%d",&c[i]);
        sort(c+1,c+n+1);
        for(int i=1;i<=n-k+1;i++)
        {
            ans = (ans + 1ll * c[i] * comb(n-i,k-1)%p)%p;
        }
        cout << ans << endl;
        return 0;
    }
    
  • 1

信息

难度
8
分类
(无)
标签
(无)
递交数
30
已通过
5
通过率
17%
上传者