数学真可怕。
Source
https://www.lydsy.com/JudgeOnline/problem.php?id=4870
Solution
首先,组合数有一个重要的性质:
Cmn=Cmn−1+Cm−1n−1
C
n
m
=
C
n
−
1
m
+
C
n
−
1
m
−
1
设:
f(m,r)=∑i=0∞Cik+rm
f
(
m
,
r
)
=
∑
i
=
0
∞
C
m
i
k
+
r
根据组合数的性质推出:
f(m,r)=∑i=0∞(Cik+rm−1+Cik+(r+k−1)modkm−1)
f
(
m
,
r
)
=
∑
i
=
0
∞
(
C
m
−
1
i
k
+
r
+
C
m
−
1
i
k
+
(
r
+
k
−
1
)
mod
k
)
=∑i=0∞Cik+rm−1+∑i=0∞Cik+(r+k−1)modkm−1
=
∑
i
=
0
∞
C
m
−
1
i
k
+
r
+
∑
i
=
0
∞
C
m
−
1
i
k
+
(
r
+
k
−
1
)
mod
k
=f(m−1,r)+f(m−1,(r+k−1)modk)
=
f
(
m
−
1
,
r
)
+
f
(
m
−
1
,
(
r
+
k
−
1
)
mod
k
)
边界 f(0,0)=1 f ( 0 , 0 ) = 1 。
由于 k k 不大,因此用矩阵乘法就能解决这个问题。
Code
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define For(i, a, b) for (i = a; i <= b; i++)
using namespace std; typedef long long ll;
const int N = 55;
int n, PYZ, k, r;
struct cyx {
int n, m, a[N][N]; cyx() {} cyx(int _n, int _m) : n(_n), m(_m)
{memset(a, 0, sizeof(a));}
friend inline cyx operator * (const cyx &a, const cyx &b) {
int i, j, k; cyx res = cyx(a.n, b.m);
For (i, 1, res.n) For (j, 1, res.m) For (k, 1, a.m)
res.a[i][j] = (res.a[i][j] + 1ll * a.a[i][k] * b.a[k][j] % PYZ) % PYZ;
return res;
}
friend inline cyx operator ^ (cyx a, ll b) {
int i; cyx res = cyx(a.n, a.m); For (i, 1, res.n) res.a[i][i] = 1;
while (b) {
if (b & 1) res = res * a; a = a * a; b >>= 1;
}
return res;
}
} P, Y, Z;
int main() {
int i; cin >> n >> PYZ >> k >> r; P = cyx(k, k);
For (i, 0, k - 1) P.a[i + 1][i + 1]++, P.a[i + 1][(i + k - 1) % k + 1]++;
Y = cyx(1, k); Y.a[1][1] = 1; Z = Y * (P ^ 1ll * n * k);
cout << Z.a[1][r + 1] << endl; return 0;
}