题目描述

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

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

现在小甲苯想要写下神的指示,记为字符串 \(s_2\),\(s_2\) 仅包含小写字母 \(\texttt{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\times 26\)的矩阵。

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

那么我们把\(f[i]\)视为一个\(1\times 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\n", sum);
}

LOJ#3104「TJOI2019」甲苯先生的字符串的更多相关文章

  1. LG5337/BZOJ5508 「TJOI2019」甲苯先生的字符串 线性动态规划+矩阵加速

    问题描述 LG5337 BZOJ5508 题解 设\(opt_{i,j}(i \in [1,n],j \in [1,26])\)代表区间\([1,i]\),结尾为\(j\)的写法. 设\(exist_ ...

  2. 【LOJ】#3109. 「TJOI2019」甲苯先生的线段树

    LOJ#3109. 「TJOI2019」甲苯先生的线段树 发现如果枚举路径两边的长度的话,如果根节点的值是$x$,左边走了$l$,右边走了$r$ 肯定答案会是$(2^{l + 1} + 2^{r + ...

  3. LOJ #6436. 「PKUSC2018」神仙的游戏(字符串+NTT)

    题面 LOJ #6436. 「PKUSC2018」神仙的游戏 题解 参考 yyb 的口中的长郡最强选手 租酥雨大佬的博客 ... 一开始以为 通配符匹配 就是类似于 BZOJ 4259: 残缺的字符串 ...

  4. LG5338/BZOJ5509/LOJ3105 「TJOI2019」甲苯先生的滚榜 Treap

    问题描述 LG5338 LOJ3105 BZOJ5509 题解 建立一棵\(\mathrm{Treap}\),把原来的\(val\)换成两个值\(ac,tim\) 原来的比较\(val_a<va ...

  5. 「TJOI2019」甲苯先生的滚榜

    题目链接 问题分析 参照数据范围,我们需要一个能够在\(O(n\log n)\)复杂度内维护有序数列的数据结构.那么平衡树是很好的选择.参考程序中使用带旋Treap. 参考程序 #pragma GCC ...

  6. @loj - 2106@ 「JLOI2015」有意义的字符串

    目录 @description@ @solution@ @accepted code@ @details@ @description@ B 君有两个好朋友,他们叫宁宁和冉冉.有一天,冉冉遇到了一个有趣 ...

  7. Loj #2192. 「SHOI2014」概率充电器

    Loj #2192. 「SHOI2014」概率充电器 题目描述 著名的电子产品品牌 SHOI 刚刚发布了引领世界潮流的下一代电子产品--概率充电器: 「采用全新纳米级加工技术,实现元件与导线能否通电完 ...

  8. Loj #3096. 「SNOI2019」数论

    Loj #3096. 「SNOI2019」数论 题目描述 给出正整数 \(P, Q, T\),大小为 \(n\) 的整数集 \(A\) 和大小为 \(m\) 的整数集 \(B\),请你求出: \[ \ ...

  9. Loj #3093. 「BJOI2019」光线

    Loj #3093. 「BJOI2019」光线 题目描述 当一束光打到一层玻璃上时,有一定比例的光会穿过这层玻璃,一定比例的光会被反射回去,剩下的光被玻璃吸收. 设对于任意 \(x\),有 \(x\t ...

随机推荐

  1. 错误:net::ERR_BLOCKED_BY_CLIENT

    提示net::ERR_BLOCKED_BY_CLIENT错误 解决办法: 当我们查看浏览器第三方插件的时候,就会看到插件中存在“广告过滤插件”,其实,报错的原因就是第三方广告过滤插件在捣鬼, 当你关闭 ...

  2. (C#)Application.Exit()、Environment.Exit(0)区别

    Application.Exit:通知winform消息循环退出.会在所有前台线程退出后,退出应用 强行退出方式,就像 Win32 的 PostQuitMessage().它意味着放弃所有消息泵,展开 ...

  3. String字符串相关操作

    .length 字符串长度.equals 比较字符串.equalIgnoreCase 比较字符串不区别大小写.charAt 获取字符串指定下标位置的字符.contains 判断字符串内是否包含某字符串 ...

  4. python的安装与配置

    pyhton的下载与安装 1.python官网地址:https://www.python.org 2.下载 Python 编辑器PyCharm PyCharm 是一款功能强大的 Python 编辑器 ...

  5. Java 8 集合之流式(Streams)操作, Streams API 详解

    因为当时公司的业务需要对集合进行各种各样的业务逻辑操作,为了提高性能,就用到了这个东西,因为以往我们以前用集合都是需要去遍历(串行),所以效率和性能都不是特别的好,而Streams就可以使用并行的方式 ...

  6. Linux学习-防火墙-Selinux-配置本地YUM源

    关闭防火墙并设置开机不启动 systemctl status firewalld.service #查看firewalld状态systemctl stop firewalld #关闭systemctl ...

  7. spring框架学习(二)——注解方式IOC/DI

    什么是注解 传统的Spring做法是使用.xml文件来对bean进行注入或者是配置aop.事物,这么做有两个缺点: 1.如果所有的内容都配置在.xml文件中,那么.xml文件将会十分庞大:如果按需求分 ...

  8. centos将nginx服务设置为开机自动启动

    centos将nginx服务设置为开机自动启动 1.在/etc/init.d下创建文件nginx 发现原来就有并且配置就是默认的,而且是正确的 命令为: vim /etc/init.d/nginx 需 ...

  9. 16 IO流(十三)——Object流 序列化与反序列化

    Object流.序列化与反序列化 Object流是将 可序列化的对象 进行序列化与反序列化的流. 可序列化的对象:使用关键字Serializable修饰,表示这个对象可以进行序列化与反序列化. 序列化 ...

  10. xorm -Find方法实例

    查询多条数据使用Find方法,Find方法的第一个参数为slice的指针或Map指针,即为查询后返回的结果,第二个参数可选,为查询的条件struct的指针. package main import ( ...