给你两个字符串 l , r ,初始时 l=′0′,r=′1′ ,然后我们有 N 轮操作,每轮操作把 r 变成原来的 l 加上原来 r (即连接上),把 l 变成原来的 r 。
给一个长度为 M 的 01 串 s ,问操作完后的 l 串包含多少个 s ,答案对 p 取模。
N≤109 M≤104 0<p≤109
我们设 Ansl 为 l 串种包含 s 串的数量, Ansr 表示 r 串种包含 s 串的数量。显然每一轮我们可以把新串的 Ansl 变成 Ansr , Ansr 变成 Ansl+Ansr+T , T 表示 l 串和 r 串拼接处产生的 s 串的数量,我们只需保留 l 串的后 M−1 位和 r 的前 M−1 位,拼起来跑一次 KMP 就可以了。
可是 N 很大,我们怎么处理。我们列出 l 和 r ,当它们的长度都至少是 2M−2 时我们发现 l 与 r 交接处的字符串是有周期性的(在纸上画画就能发现),并且只有交错出现的两种情况,那么球可以确定 T 的值了,那么再用矩阵乘法跑一跑,因为矩阵乘法是满足结合律的,就可以把两个转移矩阵合起来做。
总结一下,就是暴力跑到长度大于 2M−2 ,再用矩阵乘法快速算出答案。
由于比赛时调程序是时比较紧张,导致代码打的很丑。。。。。。真的很丑!
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long LL;
const int MAXS = 1e4 + 5;
struct Matrix {
int arr[5][5];
} Mar, Mar2, Ans;
char S[MAXS + 1], L[MAXS * 100], R[MAXS * 100], Tmp[MAXS * 5], A[MAXS * 2], B[MAXS * 2], C[MAXS * 2];
int N, Mo, Lenl, Lenr, Lens, Ansl, Ansr, P[MAXS];
Matrix operator * (Matrix a, Matrix b) {
Matrix c;
memset(c.arr, 0, sizeof c.arr);
for (int i = 1; i <= 3; i ++)
for (int j = 1; j <= 3; j ++)
for (int k = 1; k <= 3; k ++)
c.arr[i][j] = (LL(c.arr[i][j]) + 1ll * a.arr[i][k] * b.arr[k][j]) % Mo;
return c;
}
Matrix Power(Matrix x, int y) {
Matrix c;
memset(c.arr, 0, sizeof c.arr);
for (int i = 1; i <= 3; i ++) c.arr[i][i] = 1;
for (; y; y >>= 1, x = x * x)
if (y & 1) c = c * x;
return c;
}
int GetAns(char *L, char *R) {
int l = strlen(L + 1), r = strlen(R + 1);
int t = min(l, Lens - 1), Cnt = t;
for (int i = l; Cnt; i --, Cnt --) Tmp[Cnt] = L[i];
for (int i = 1; i < Lens && i <= r; i ++) Tmp[++ t] = R[i];
int Lst = 0, Ans = 0;
for (int i = 1; i <= t; i ++) {
while (Lst && S[Lst + 1] != Tmp[i]) Lst = P[Lst];
if (S[Lst + 1] == Tmp[i]) Lst ++;
if (Lst == Lens) {
Ans = (Ans + 1) % Mo;
Lst = P[Lst];
}
}
return Ans;
}
void GetKmp() {
P[1] = 0;
int Lst = 0;
for (int i = 2; i <= Lens; i ++) {
while (Lst && S[Lst + 1] != S[i]) Lst = P[Lst];
if (S[Lst + 1] == S[i]) Lst ++;
P[i] = Lst;
}
}
int main() {
scanf("%d%d%d", &N, &Lens, &Mo);
scanf("%s", S + 1);
Lenl = Lenr = 1, L[1] = '0', R[1] = '1';
if (Lens == 1) {
if (S[1] == '1') Ansr = 1; else Ansl = 1;
}
int Cnt = 0;
GetKmp();
while (Lenl < 2 * Lens && Cnt < N) {
Cnt ++;
int Ans = GetAns(L, R);
swap(Lenl, Lenr);
swap(Ansl, Ansr), Ansr = ((Ansl + Ansr) % Mo + Ans) % Mo;
swap(L, R);
for (int i = 1; i <= Lenl; i ++) R[Lenr + i] = L[i];
Lenr = Lenr + Lenl;
}
if (N == Cnt) {
printf("%d", Ansl % Mo);
return 0;
}
N = N - Cnt;
for (int i = 1; i < Lens; i ++) A[i] = L[i];
for (int i = Lenl, Cnt = Lens - 1; Cnt; i --, Cnt --) B[Cnt] = L[i];
for (int i = 1; i < Lens; i ++) C[i] = R[i];
Mar.arr[1][1] = 0, Mar.arr[2][1] = 1, Mar.arr[3][1] = 0;
Mar.arr[1][2] = 1, Mar.arr[2][2] = 1, Mar.arr[3][2] = GetAns(B, C);
Mar.arr[1][3] = 0, Mar.arr[3][3] = 1, Mar.arr[3][3] = 1;
Mar2.arr[1][1] = 0, Mar2.arr[2][1] = 1, Mar2.arr[3][1] = 0;
Mar2.arr[1][2] = 1, Mar2.arr[2][2] = 1, Mar2.arr[3][2] = GetAns(B, A);
Mar2.arr[1][3] = 0, Mar2.arr[3][3] = 1, Mar2.arr[3][3] = 1;
Mar2 = Mar * Mar2;
Ans = Power(Mar2, N / 2);
if (N & 1) Ans = Ans * Mar;
int SS = 0;
SS = 1ll * Ansl * Ans.arr[1][1] % Mo;
SS = (LL(SS) + 1ll * Ansr * Ans.arr[2][1] % Mo) % Mo;
SS = (LL(SS) + Ans.arr[3][1]) % Mo;
printf("%d\n", SS);
}