[TJOI2019] 甲苯先生的字符串 题解
T2 [TJOI2019] 甲苯先生的字符串
矩阵乘法优化 DP,所以一般来说这种 DP 都不怎么难。
30pts 的 DP 是裸的:设 \(f_{i,j}\) 为前 \(i\) 位、最后一位是 \(j\) 的方案数,则有转移方程:
\[f_{i,j}=\sum_{k=0}^{25}f_{i-1,k}\land k\ne j
\]
想要矩阵优化,我们想到构造答案矩阵:
\[\mathit{ans}=\underbrace{\begin{bmatrix}1&1&1&\cdots&1\end{bmatrix}}_{\text{26个1}}
\]
构造转移矩阵:
\[\mathit{base}=\begin{bmatrix}1&1&1&\cdots&1\\1&1&1&\cdots&1\\1&1&1&\cdots&1\\\vdots&\vdots&\vdots&\ddots&\vdots\\1&1&1&\cdots&1\end{bmatrix}
\]
然后把不能相邻的位置点成 \(0\)。最终答案就是 \(\mathit{ans}\times\mathit{base}^{n-1}\) 之后 \(\sum\mathit{ans}_{0,i}\)。
#include<bits/stdc++.h>
#define int long long
using namespace std;
constexpr int MAXN=1e5+5,MOD=1e9+7;
int n;
string s1;
void add(int&x,int y){
x=x+y>=MOD?x+y-MOD:x+y;
}
struct Matrix{
int mx[26][26];
Matrix(int x=0){
memset(mx,0,sizeof(mx));
if(x)for(int i=0;i<26;++i)mx[i][i]=x;
}
int*operator[](const int&x){
return mx[x];
}
Matrix operator*(const Matrix&b)const{
Matrix res;
for(int i=0;i<26;++i)
for(int k=0;k<26;++k)
for(int j=0;j<26;++j)
add(res[i][j],mx[i][k]*b.mx[k][j]%MOD);
return res;
}
}ans,base;
void print(Matrix x){
for(int i=0;i<26;++i,cout<<'\n')
for(int j=0;j<26;++j)
cout<<x[i][j]<<' ';
}
Matrix power(Matrix a,int b){
Matrix res=Matrix(1);
for(;b;a=a*a,b>>=1)if(b&1)res=res*a;
return res;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(nullptr),cout.tie(nullptr);
cin>>n>>s1;
for(int i=0;i<26;++i){
ans[0][i]=1;
for(int j=0;j<26;++j)base[i][j]=1;
}
for(int i=1;s1[i];++i)base[s1[i-1]-'a'][s1[i]-'a']=0;
ans=ans*power(base,n-1);
int res=0;
for(int i=0;i<26;++i)add(res,ans[0][i]);
cout<<res<<'\n';
return 0;
}
噫吁嚱,危乎高哉!蜀道之难,难于上青天!
蚕丛及鱼凫,开国何茫然!
尔来四万八千岁,不与秦塞通人烟。
西当太白有鸟道,可以横绝峨眉巅。
地崩山摧壮士死,然后天梯石栈相钩连。
上有六龙回日之高标,下有冲波逆折之回川。
黄鹤之飞尚不得过,猿猱欲度愁攀援。
青泥何盘盘,百步九折萦岩峦。
扪参历井仰胁息,以手抚膺坐长叹。
问君西游何时还?畏途巉岩不可攀。
但见悲鸟号古木,雄飞雌从绕林间。
又闻子规啼夜月,愁空山。
蜀道之难,难于上青天,使人听此凋朱颜。
连峰去天不盈尺,枯松倒挂倚绝壁。
飞湍瀑流争喧豗,砯崖转石万壑雷。
其险也如此,嗟尔远道之人胡为乎来哉!
剑阁峥嵘而崔嵬,一夫当关,万夫莫开。
所守或匪亲,化为狼与豺。
朝避猛虎,夕避长蛇。
磨牙吮血,杀人如麻。
锦城虽云乐,不如早还家。
蜀道之难,难于上青天,侧身西望长咨嗟!