bzoj 4338[BJOI2015] 糖果 - 组合
4338: BJOI2015 糖果
Time Limit: 2 Sec Memory Limit: 256 MBDescription
Alice 正在教她的弟弟 Bob 学数学。
每天,Alice 画一个N行M 列的表格,要求 Bob在格子里填数。
Bob已经学会了自然数1到K的写法。因此他在每个格子里填1 ~ K之间的整数。
Alice 告诉 Bob,如果 Bob 填写完表格的 N*M 个数以后,每行的数从第 1 列到第 M
列单调不减,并且任意两行至少有一列的数不同,而且以前 Bob 没有填写过相同的表格,
那么Alice 就给Bob吃一颗糖果。
Bob想知道,如果每天填写一遍表格,最多能吃到多少颗糖果。
答案模P输出。
Input
第一行,四个整数依次是N, M, K, P。
Output
输出一行,一个整数,表示答案模P 后的结果。
Sample Input
【样例输入1】
1 3 3 10
【样例输入2】
2 2 2 10
1 3 3 10
【样例输入2】
2 2 2 10
Sample Output
【样例输出1】
0
【样例输出2】
6
0
【样例输出2】
6
HINT
【样例解释】
样例1。表格只有一行。每个格子可以填写1 ~ 3。有10种填写方法,依次为1 1 1,
1 1 2,1 1 3,1 2 2,1 2 3,1 3 3,2 2 2,2 2 3,2 3 3,3 3 3。
样例2。表格有两行。有6 种填写方法,依次为 1 1/1 2, 1 1/2 2, 1 2/1 1, 1 2/2
2, 2 2/1 1, 2 2/1 2。
【数据规模与约定】
100% 的数据中,1 ≤ N, M ≤ 10^5,1 ≤ P, K ≤ 2*10^9.
公式很好写出来
先算出来一行 M 列 填 K 单调不减个数的方法数
即 x1 + x2 + ... xk = M (xi >= 0)
方案数 为 sum = $C_{M + K - 1} ^ {K - 1}$ = $C_{M + K - 1} ^ {M}$
每行方案不同
总的方案就是 $A_{sum} ^ N$
我们发现 M 不大, 所以可以除数的所有质因子都提出来,然后用分子上的数去除,剩下的就是答案。
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 #define LL long long 6 7 using namespace std; 8 9 int N, M, K, P; 10 11 const int MAXM = 1e5 + 10; 12 13 int cnt = 0; 14 int prime[MAXM]; 15 LL val[MAXM]; 16 int num[MAXM]; 17 int f[MAXM]; 18 19 inline LL read() 20 { 21 LL x = 0, w = 1; char ch = 0; 22 while(ch < '0' || ch > '9') { 23 if(ch == '-') { 24 w = -1; 25 } 26 ch = getchar(); 27 } 28 while(ch >= '0' && ch <= '9') { 29 x = x * 10 + ch - '0'; 30 ch = getchar(); 31 } 32 return x * w; 33 } 34 35 void init() 36 { 37 for(int i = 2; i < MAXM; i++) { 38 if(!f[i]) { 39 f[i] = i; 40 prime[++cnt] = i; 41 } 42 for(int j = 1; j <= cnt && i * prime[j] < MAXM; j++) { 43 f[i * prime[j]] = prime[j]; 44 if(i % prime[j] == 0) { 45 break; 46 } 47 } 48 } 49 /*for(int i = 0; i <= 2000; i++) { 50 c[i][0] = 1; 51 for(int j = 1; j <= i; j++) { 52 c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % P; 53 } 54 }*/ 55 } 56 57 int main() 58 { 59 N = read(), M = read(), K = read(), P = read(); 60 init(); 61 for(int j = 0; j < M; j++) { 62 val[j] = K + j; 63 } 64 for(int j = M; j > 1; j--) { 65 int t = j; 66 while(t > 1) { 67 num[f[t]]++; 68 t = t / f[t]; 69 } 70 } 71 int st = K; 72 for(int i = 1; i <= cnt; i++) { 73 for(int j = ((st - 1) / prime[i] + 1) * prime[i]; j < K + M; j += prime[i]) { 74 int t = j - st; 75 //cout<<prime[i]<<" "<<j<<" "<<val[t]<<" "<<num[prime[i]]<<endl; 76 while(val[t] % prime[i] == 0 && num[prime[i]]) { 77 val[t] /= prime[i]; 78 num[prime[i]]--; 79 } 80 if(num[prime[i]] == 0) { 81 break; 82 } 83 } 84 } 85 LL ans = 1; 86 for(int i = 0; i < M; i++) { 87 ans = ans * val[i] % P; 88 } 89 //cout<<ans<<" "<<c[M + K - 1][K - 1] % P<<endl; 90 LL now = ans; 91 for(int i = 1; i < N; i++) { 92 ans = ans * (now - i) % P; 93 } 94 printf("%lld\n", ans); 95 return 0; 96 } 97 98 /* 99 100 100 10 5 1000000000 101 102 103 */