BZOJ 4870: [Shoi2017]组合数问题 (递推+矩阵快速幂)

来源:互联网 发布:在淘宝订的机票可信吗 编辑:程序博客网 时间:2024/06/03 11:38

Description

这里写图片描述


Input

第一行有四个整数 n, p, k, r,所有整数含义见问题描述。
1 ≤ n ≤ 10^9, 0 ≤ r < k ≤ 50, 2 ≤ p ≤ 2^30 − 1


Output

一行一个整数代表答案。


Sample Input

2 10007 2 0


Sample Output

8

这里写图片描述

Source

黑吉辽沪冀晋六省联考


分析

这题。。我没有什么好说的。简直了。。

只要读懂题意。。

人话题意:从n*k个物品里选模k余r个物品,问方案数模p。

sb题啊,大水题啊。。然而考场上蒟蒻我一头雾水地面对题目、手忙脚乱地写了一堆暴力(又逆元又快速幂的,还有人写了线性逆元。。),水了70分(虽说我已经很满足了),但被水题刷,实在不甘心。。

做法就不说了,记f[i][j]表示前i个物品选模p余j个,然后就是一水到爆的递推嘛,加上矩阵快速幂优化就行了。。
(还是太弱了/(ㄒoㄒ)/~~)

还是有两大坑点:

  • Ⅰ.k=1 时的矩阵初始化
  • Ⅱ.快速幂不要用递归写,要用循环写,否则会炸(真奇怪,真奇怪)

代码

#include <iostream>#include <cstdio>#include <cstdlib>#include <cstring>#include <algorithm>#include <cmath>#include <ctime>#define N 3218using namespace std;typedef long long LL;int n, K, r, p;struct Mat{    int x, y;    LL num[55][55];    void Clear(){      memset(num, 0, sizeof(num));    }}A, B, Ans;Mat operator * (Mat A, Mat B){    Mat C;    C.Clear();    C.x = A.x;    C.y = B.y;    for(int i = 0; i < C.x; i++)     for(int j = 0; j < C.y; j++)      for(int k = 0; k < A.y; k++)        C.num[i][j] = (C.num[i][j] + (A.num[i][k] % p * B.num[k][j] % p)) % p;    return C;}Mat Pow(Mat X, LL n){    while(n){      if(n & 1)  A = X * A;      X = X * X;      n >>= 1;    }    return A;}int main(){    freopen("problem.in", "r", stdin);    freopen("problem.out", "w", stdout);    scanf("%d%d%d%d", &n, &p, &K, &r);    A.Clear();    B.Clear();    Ans.Clear();    A.x = K;    A.y = 1;    A.num[0][0] = 1;    B.x = B.y = K;    for(int i = 0; i < K; i++){      B.num[i][i] = 1;      B.num[i][(i+1)%K] ++;    }    Ans = Pow(B, (LL)(n) * (LL)K);//注意转LL    printf("%lld\n", Ans.num[r][0]);    return 0;}

有一天 春花秋月 夏蝉冬雪 不会散去

1 0
原创粉丝点击