GDOI2016模拟3.9 暴走的图灵机 矩阵乘法优化暴力

题目大意

给你两个字符串 l r ,初始时 l=0,r=1 ,然后我们有 N 轮操作,每轮操作把 r 变成原来的 l 加上原来 r (即连接上),把 l 变成原来的 r
给一个长度为 M 01 s ,问操作完后的 l 串包含多少个 s ,答案对 p 取模。

N109    M104    0<p109

解题思路

我们设 Ansl l 串种包含 s 串的数量, Ansr 表示 r 串种包含 s 串的数量。显然每一轮我们可以把新串的 Ansl 变成 Ansr Ansr 变成 Ansl+Ansr+T T 表示 l 串和 r 串拼接处产生的 s 串的数量,我们只需保留 l 串的后 M1 位和 r 的前 M1 位,拼起来跑一次 KMP 就可以了。

可是 N 很大,我们怎么处理。我们列出 l r ,当它们的长度都至少是 2M2 时我们发现 l r 交接处的字符串是有周期性的(在纸上画画就能发现),并且只有交错出现的两种情况,那么球可以确定 T 的值了,那么再用矩阵乘法跑一跑,因为矩阵乘法是满足结合律的,就可以把两个转移矩阵合起来做。

总结一下,就是暴力跑到长度大于 2M2 ,再用矩阵乘法快速算出答案。

代码

由于比赛时调程序是时比较紧张,导致代码打的很丑。。。。。。真的很丑!

#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);
}

你可能感兴趣的:(GDOI2016模拟3.9 暴走的图灵机 矩阵乘法优化暴力)