Loading

[TJOI2019] 甲苯先生的字符串

算法

一眼计数 \(\rm{dp}\) , 不会, 下播

好的让我们分析一下题意

问题可以简单地转化为对于每一个字母, 只能选择另外的某些字母作为下一个, 求一共的可能性

简单的, 我们找出 \(\rm{dp}\) 方程, 令 \(f_{i, s}\) 表示考虑前 \(i\) 个字母, 以 \(s\) 结尾的可能性数量, 转移不用多说

\[f_{i, s} = \sum f_{i - 1, s^{\prime}} \]

所以怎么快速转移这个柿子

矩阵艹一下, 过了

实现

矩阵入门题, 打一下过个板子

框架

首先预处理每一种字符可以由什么字符转移而来, 然后建矩阵

代码

#include <bits/stdc++.h>
#define int long long
const int MAXN = 30;
const int MOD = 1e9 + 7;

int n;

struct Matrix {
    int mar[MAXN][MAXN];
    bool type; // false 表示 n * n , true 表示 n * 1
} ;

Matrix operator * (const Matrix& a, const Matrix& b) {
    Matrix ans; memset(ans.mar, 0, sizeof(ans.mar));
    int tmp; tmp = (a.type || b.type) ? 1 : n;

    for (int i = 1; i <= 26; i++)
        for (int j = 1; j <= 26; j++) 
            for (int k = 1; k <= 26; k++) {
                ans.mar[i][j] += (a.mar[i][k] % MOD) * (b.mar[k][j] % MOD);
                ans.mar[i][j] %= MOD;
            }

    return ans;
}

Matrix fastpow(Matrix a, int pow) {
    Matrix ans;
    memset(ans.mar, 0, sizeof(ans.mar)); for (int i = 1; i <= 26; i++) ans.mar[i][i] = 1;

    while (pow) {
        if (pow & 1) ans = ans * a; a = a * a;
        pow >>= 1; 
    }

    return ans;
}

std::string s1;
bool CanTurn[30][30];
Matrix ini, powmar;
void init() {
    memset(CanTurn, true, sizeof(CanTurn));
    for (int i = 1; i < s1.size(); i++)
        CanTurn[s1[i] - 'a' + 1][s1[i - 1] - 'a' + 1] = false;

    for (int i = 1; i <= 26; i++) ini.mar[i][1] = 1; ini.type = true;
    for (int i = 1; i <= 26; i++)
        for (int j = 1; j <= 26; j++) 
            if (CanTurn[i][j]) powmar.mar[i][j] = 1; else powmar.mar[i][j] = 0;
}

signed main()
{
    scanf("%lld", &n);
    std::cin >> s1;

    init();
    powmar = fastpow(powmar, n - 1);
    powmar = powmar * ini;
    int Ans = 0;
    for (int i = 1; i <= 26; i++) {
        Ans += powmar.mar[i][1];
        Ans %= MOD;
    }
    printf("%lld", Ans);

    return 0;
}

总结

不好评价的简单题, 只是说我不会打矩乘

记得取模

posted @ 2024-12-08 20:25  Yorg  阅读(4)  评论(0编辑  收藏  举报