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; }