P4456 CQOI2018 交错序列 - 洛谷 (luogu.com.cn)
0.分析
所谓的交错序列,如果只看数量,有:
cpp
f[n]=f[n-1]+f[n-2]
即在
后面放
,在
后面放
。
现在假设增加一个 ,
。
增加 同理,
发现转移的时候要控制两边的二项式,麻烦,考虑换成确定末尾 的状态。
即定义 表示长度为
的序列,最后的位为
。
推
等于
前面这玩意是个固定的系数!太好了,我们只用处理
。
化简一下,设 ,
,也就是我们只用在意
的个数。


如果直接线性递推,时间复杂度 ,寄。
1.设计



2.代码
时间复杂度 。
cpp
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 200;
LL n, P; int K;
struct Matrix {
LL a[N][N];
Matrix() {
memset(a, 0, sizeof(a));
}
};
LL C[N][N];
Matrix operator*(const Matrix& a, const Matrix& b) {
Matrix c;
for (int i = 0; i <= 2 * K + 1; i++) {
for (int k = 0; k <= 2 * K + 1; k++) {
LL aik = a.a[i][k];
if (!aik) continue;
for (int j = 0; j <= 2 * K + 1; j++) {
c.a[i][j] += aik * b.a[k][j];
}
}
for (int j = 0; j <= 2 * K + 1; j++) {
c.a[i][j] %= P;
}
}
return c;
}
Matrix q_pow(Matrix a, LL b) {
Matrix c;
for (int i = 0; i <= 2 * K + 1; i ++) {
c.a[i][i] = 1;
}
while (b) {
if (b & 1) {
c = c * a;
}
a = a * a;
b >>= 1;
}
return c;
}
void init() {
memset(C, 0, sizeof(C));
C[0][0] = 1;
for (int i = 0; i <= K; i ++) {
C[i][0] = C[i][i] = 1;
for (int j = 1; j < i; j ++) {
C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % P;
}
}
}
int main () {
ios::sync_with_stdio(false);
cin.tie(0);
int a, b;
cin >> n >> a >> b >> P;
K = a + b;
init();
Matrix m, x, res;
x.a[0][0] = 1;
for (int i = K + 1; i <= 2 * K + 1; i ++) {
x.a[i][0] = 1;
}
for (int i = 0; i <= K; i ++) {
m.a[i][i] = 1;
m.a[i][i + K + 1] = 1;
}
for (int i = 0; i <= K; i ++) {
for (int j = 0; j <= i; j ++) {
m.a[i + K + 1][j] = C[i][j];
}
}
res = q_pow(m, n - 1) * x;
LL ans = 0;
LL n_pow = 1;
for (int i = 0; i <= a; i ++) {
LL S = (res.a[K - i][0] + res.a[2 * K + 1 - i][0]) % P;
LL term = C[a][i] * n_pow % P;
term = term * S % P;
if ((a - i) & 1) {
ans = (ans - term + P) % P;
}
else {
ans = (ans + term) % P;
}
n_pow = n_pow * n % P;
}
cout << ans % P << "\n";
return 0;
}