zoukankan      html  css  js  c++  java
  • LOJ#3104「TJOI2019」甲苯先生的字符串

    题目描述

    一天小甲苯得到了一条神的指示,他要把神的指示写下来,但是又不能泄露天机,所以他要用一种方法把神的指示记下来。

    神的指示是一个字符串,记为字符串 (s_1)(s_1) 仅包含小写字母 ( exttt{a-z})

    现在小甲苯想要写下神的指示,记为字符串 (s_2)(s_2) 仅包含小写字母 ( exttt{a-z}),要求 (s_1) 中的相邻的两个字母不能在 (s_2) 中相邻地出现。

    现在给定 (s_2) 的长度,小甲苯想知道他有多少种方法可以将神的指示写下来。

    输出种类数对 (10^9 + 7) 取模的结果。

    输入格式

    文件的第一行只有一个正整数 (n),代表字符串 (s_2) 的长度,(n le 10^{15})

    第二行是一个字符串,代表字符串 (s_1)(s_1) 的长度不超过 (100000)

    输出格式

    输出一个整数,表示小甲苯可以写出的字符串的总数。

    结果对 (10^9 + 7) 取模。

    样例

    样例输入

    2
    ab
    

    样例输出

    675
    

    数据范围与提示

    对于 (30\%) 的数据,(n ≤ 100000)

    对于 (100\%) 的数据,(n ≤ 10^{15})

    题解

    我的做法其实应该和网上的题解差不多。不过理解起来可能需要比较感性一点?

    因为考虑的只有相邻两个字符,不难得知,可以把所有关系写成一个(26 imes 26)的矩阵。

    先思考一下正常怎么求解:(f[i][j]=sum f[i-1][k]*a[k][j])(i)位第几位数字,(j)为当前放的字母,(k)为上一位放的字母)

    那么我们把(f[i])视为一个(1 imes n)的矩阵,实际上这就是一个矩阵乘法的过程。

    利用矩阵快速幂求解即可。

    但是我构造的初始的矩阵有点不同:初始矩阵为一个全(1)的矩阵,最后统计答案时答案为(sum f[i][i])

    怎么理解呢?实际上就是第一位怎么放都行,然后矩阵乘法得到的矩阵中的(c[i][j]),实际上就是第一个矩阵的第(i)行与第二个矩阵的第(j)列的答案。所以最终的(f[i][i])即为第一位填(i)可以得到的方案数。

    那么为什么要这么构造就不难理解了。

    #include <bits/stdc++.h>
    using namespace std;
    
    #define ll long long
    const int N = 100010;
    const ll mod = 1e9 + 7;
    
    ll n;
    char s[N];
    struct mat {
        ll m[26][26];
    };
    
    mat operator * (mat a, mat b) {
        mat c;
        memset(c.m, 0, sizeof(c.m));
        for(int i = 0; i < 26; ++i) {
            for(int j = 0; j < 26; ++j) {
                for(int k = 0; k < 26; ++k) {
                    c.m[i][j] = (c.m[i][j] + a.m[i][k] * b.m[k][j] % mod) % mod;
                }
            }
        }
        return c;
    }
    
    mat base, ans, now;
    
    int idx(char c) {return c - 'a';} 
    
    void print(mat a) {
        for(int i = 0; i < 26; ++i) {
            for(int j = 0; j < 26; ++j) printf("%d ", a.m[i][j]);
            puts("");
        }
    }
    
    int main() {
        scanf("%lld%s", &n, s + 1);
        for(int i = 0; i < 26; ++i) for(int j = 0; j < 26; ++j) base.m[i][j] = 1;
        int len = strlen(s + 1);
        for(int i = 1; i < len; ++i) {
            base.m[idx(s[i])][idx(s[i + 1])] = 0;
        }
    
        memset(ans.m, 0, sizeof(ans.m));
        memset(now.m, 0, sizeof(now.m));
        for(int i = 0; i < 26; ++i) ans.m[i][i] = 1;
    
        --n;
        while(n) {
            if(n & 1) ans = ans * base;
            base = base * base; n >>= 1;
        }
        for(int i = 0; i < 26; ++i) for(int j = 0; j < 26; ++j) now.m[i][j] = 1;
        now = now * ans;
    
        ll sum = 0;
        for(int i = 0; i < 26; ++i) sum = (sum + now.m[i][i]) % mod;
        printf("%lld
    ", sum);
    }
    
  • 相关阅读:
    P1113 杂务 题解
    P3916 图的遍历 题解
    P5318 【深基18.例3】查找文献 题解
    P2814 家谱 题解
    P3879 [TJOI2010]阅读理解 题解
    P4305 不重复的数字题解
    P1955 [NOI2015] 程序自动分析题解
    P1892 [BOI2003]团伙
    P1525 [NOIP2010 提高组] 关押罪犯
    【610】keras 相关问题说明
  • 原文地址:https://www.cnblogs.com/henry-1202/p/10889922.html
Copyright © 2011-2022 走看看