bzoj 4338[BJOI2015] 糖果 - 组合

4338: BJOI2015 糖果

Time Limit: 2 Sec  Memory Limit: 256 MB

Description

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

Sample Output

【样例输出1】
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 */
View Code

 

posted @ 2018-03-18 23:00  大财主  阅读(225)  评论(0编辑  收藏  举报