FJUT OJ 2571 stone (组合数学)

Problem Description

终于解出了dm同学的难题,dm同学同意帮v神联络。可dm同学有个习惯,就是联络同学的时候喜欢分组联络,而且分组的方式也很特别,要求第i组的的人数必须大于他指定的个数ci。在dm同学联络的时候,v神在想,按照dm同学的规则一共可以有多少种方案呢?他想啊想,终于……没想出来。于是他又想到了聪明的你,你能帮v神算出按照dm同学的规则有多少种分组方案吗?

v神的班级共有n个人,dm同学想把同学分成M组联络,要求第i组的人数必须大于给定的正整数Ci,求有多少不同的方案?(两个是相同的方案当且仅当对于任意的一队i,两个方案的第i组同学数量相等)由于结果很大,所以你只需要输出模1000000007的值。

 

Input

第一行两个整数N和M ,后面有M行,每行一个整数,表示Ci

对于100%的数据,N ,M<= 1000000 Ci<=1000

Output

仅有一行,一个整数,方案数模1000000007的值。

SampleInput
10 3
1
2
3
SampleOutput
3
样例解释:方案有三种,每堆的个数分别是(3,3,4),(2,4,4),(2,3,5)。

分析:
题目中的问题可以转化为将n-∑ci名学生分到m个小组,每个小组至少一名同学。
这样的话,运用高中的挡板法
n-∑ci-1个空位里,插入m-1个挡板即可
因为n和m的范围比较大,求组合数的时候用到卢卡斯公式

代码如下:
#include <iostream>
#include <string.h>
#include <stdio.h>

using namespace std;
typedef long long LL;

LL n,m,p;

LL quick_mod(LL a, LL b)
{
    LL ans = 1;
    a %= p;
    while(b)
    {
        if(b & 1)
        {
            ans = ans * a % p;
            b--;
        }
        b >>= 1;
        a = a * a % p;
    }
    return ans;
}

LL C(LL n, LL m)
{
    if(m > n) return 0;
    LL ans = 1;
    for(int i=1; i<=m; i++)
    {
        LL a = (n + i - m) % p;
        LL b = i % p;
        ans = ans * (a * quick_mod(b, p-2) % p) % p;
    }
    return ans;
}

LL Lucas(LL n, LL m)
{
    if(m == 0) return 1;
    return C(n % p, m % p) * Lucas(n / p, m / p) % p;
}

int main()
{
     LL sum,x;
     while(scanf("%lld%lld", &n, &m)!=EOF)
    {
        sum=0;
        p=1e9+7;
        for(int i=1;i<=m;i++){
        scanf("%lld",&x);
        sum+=x;
        }
        if(n-sum-1<=0)
        {
          puts("0");
          continue;
        }
        else
        printf("%lld\n", Lucas(n-sum-1,m-1));
    }
    return 0;
}

 

posted @ 2018-02-12 16:13  hinata_hajime  阅读(159)  评论(0编辑  收藏  举报