bzoj4870 [Shoi2017]组合数问题
传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=4870
【题解】
题目给了提示了:组合意义
题目转化为从nk个物品中选出模k余r个数的方案数。
f[i,j]表示前i个物品选出模k余j个数的方案数。
矩乘即可。
# include <stdio.h> # include <assert.h> # include <string.h> # include <algorithm> // # include <bits/stdc++.h> using namespace std; typedef long long ll; typedef long double ld; typedef unsigned long long ull; const int M = 5e5 + 10, N = 55; # define RG register # define ST static // nk个物品模k余数r的方案数 int n, mod, k, r; struct matrix { int a[N][N], n, m; inline void set(int _n, int _m) { n = _n, m = _m; memset(a, 0, sizeof a); } friend matrix operator*(matrix a, matrix b) { matrix c; assert(a.m == b.n); c.set(a.n, b.m); for (int i=1; i<=c.n; ++i) for (int j=1; j<=c.m; ++j) for (int k=1; k<=a.m; ++k) { c.a[i][j] += 1ll * a.a[i][k] * b.a[k][j] % mod; if(c.a[i][j] >= mod) c.a[i][j] -= mod; } return c; } friend matrix operator^(matrix a, ll b) { matrix I; assert(a.n==a.m); I.set(a.n, a.m); for (int i=1; i<=a.n; ++i) I.a[i][i] = 1; while(b) { if(b&1) I=I*a; a=a*a; b>>=1; } return I; } }A, T; // f[i,j]表示前i个物品选模k余j个的方案数 int main() { scanf("%d%d%d%d", &n, &mod, &k, &r); T.set(k, k); A.set(k, 1); A.a[1][1] = 1; for (int i=0; i<k; ++i) { T.a[i+1][i+1] ++; T.a[(i-1+k)%k+1][i+1]++; } ll t = (ll)n*k; A = (T^t)*A; printf("%d\n", A.a[r+1][1]); return 0; }