[Description]
山山是 2017 级信奥班的成员,因为良好(到大家都嫉妒的程度)的妹子缘而出名。山
山认识的妹子实在是太多,信奥班的各位纷纷猜测山山是怎么做到的。终于,Gemin 揭开了
这个秘密。原来,山山掌握了向妹子脑中写入程序的黑科技。山山向妹子脑中写入的程序是
一个只含有“N”、
“H”两种大写字母的字符串,每个大写字母代表一条指令;指令顺序执
行,并且在执行到结尾后立即返回起始位置,依此无限循环。每条指令都耗费一个指令周期
执行。
两种指令的含义如下:
“N”
:什么都不做;
“H”
:执行到这条指令的妹子 A 会找到一个新的妹子 B,并在妹子 B 的大脑中写入同一段
程序。在下一个指令周期开始时,妹子 B 会加入山山的后宫,并立即开始执行程序。
现在,山山的后宫中只有一个妹子 Eve,在第一个指令周期开始时,Eve 会从头开始执
行程序。现在请计算,在第 N 个指令周期结束时,山山的后宫里有多少妹子。为了避免答
案过大,请输出答案模上 998244353 的值。
[Input]
第一行一个整数:N。
下面一行,一个长度为 L 的、只含有“N”
、
“H”两种大写字母的字符串 S、代表山山会
在妹子的大脑里写入的程序。
[Output]
一行一个整数,代表在第 N 个指令周期结束时,山山的后宫里有多少妹子。
[Sample]
说明:什么都不做......
说明:虽然在第二个周期中,Eve 找到了一个新的妹子,但是新的妹子直到第三个周期的开
始才会加入后宫,因此第二个周期结束时仍然只有一个妹子。
[Tips]
我们可以把每个时刻,正在执行第几个命令的妹子数表示出来:
for(int i=1;i<=n;i++)
for(int j=0;j<len;j++)
if(S[j]=='N')
f[i+1][j+1]+=f[i][j];
if(S[j]=='H')
{
f[i+1][j+1]+=f[i][j];
f[i+1][0]+=f[i][j];
}
我们看到整个递推式,其实很好用矩阵来表示和优化。
首先需要快速幂的矩阵可以确定是一个行数为了len-1,列数为len-1的矩阵,因为每个时刻的每一位数量之和上个时刻的某个数有关。
我们先可以确定矩阵最开始的雏形,由f[i+1][j+1]+=f[i][j]得出,假设了len=4:
0 0 1
1 0 0
0 1 0
而每次碰到H,只有第0行会加上f[i][j],所以我们只用在第0行加上1就可以了。
假如当前字符串是:
NH
那我们可以构造出矩阵:
0 2
1 0
代码:
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<algorithm> #define ll long long #define il inline #define db double #define mod 998244353 using namespace std; char S[145],len; struct mul { ll a[145][145]; mul () { memset(a,0,sizeof(a)); } }hehe; il mul chen(mul x,mul y) { mul p; for(int i=0;i<len;i++) for(int j=0;j<len;j++) for(int k=0;k<len;k++) p.a[i][j]=(p.a[i][j]+x.a[i][k]*y.a[k][j])%mod; return p; } il mul pow(mul x,int b) { mul tmp; for(int i=0;i<len;i++) for(int j=0;j<len;j++) tmp.a[i][j]=0; tmp.a[0][0]=1; while(b) { if(b&1) tmp=chen(x,tmp); x=chen(x,x); b>>=1; } return tmp; } int main() { freopen("harem.in","r",stdin); freopen("harem.out","w",stdout); int n; cin>>n; cin>>S; len=strlen(S); mul T; T.a[0][len-1]=1; for(int i=1;i<len;i++) T.a[i][i-1]=1; for(int i=0;i<len;i++) if(S[i]=='H') T.a[0][i]++; mul ans=pow(T,n-1); int sum=0; for(int i=0;i<len;i++) sum=(sum+ans.a[i][0])%mod; printf("%d ",sum); return 0; }