KLAVIR
作者:互联网
题外话:蒟蒻 Feyn 在考场上成功地被这道题卡住了,两行的代码写了差不多两个小时一直憋不出来。另外有个小小的疑问,为啥其它同学最后的处理方法都和题解一毛一样呢,是我的思路过于非主流了还是什么……
约定:假设 \(A\) 是一个序列,\(a\) 是一个元素,那么 \(A+a\) 是在序列后拼上一个元素形成的新序列。\(t(A,B)\) 是指最长序列 \(C\) 的长度,满足其即是 \(A\) 的后缀又是 \(B\) 的前缀。
用 \(f_i\) 来代表弹奏完前 \(i\) 个音符的期望时间,那么我们可以把这个事件分成两个阶段。首先要弹完前 \(i\) 个音符就必须要完美地弹完前 \(i-1\) 个(这不废话),于是就必须对于最后一个音符分类讨论。显然有 \(\frac{1}{m}\) 的概率使得最后的那个音符刚好是我们想要的,此时只需要再弹一个音符即可;但更多的时候我们会选择到一个没用的音符,此时我们就回到了之前的某个状态。假设我们当前弹了的音符序列是 \(A\),当前弹错误音符是 \(a\),我们希望的音符序列是 \(B\),假如 \(len=t(A+a,B)\),那么我们就可以看成是已经弹了 \(len\) 个音符,接下来需要的期望时间理应是 \(f_i-f_{len}+1\),毕竟最后那个音符也是需要时间的。于是方程就可以列出来了:
\[f_{i}=f_{i-1}+\frac{1}{m}+\frac{1}{m}(f_i-f_{s_1})+\frac{1}{m}(f_i-f_{s_2})+\dots \]其中 \(s_1\) 代表的是序列 \(t(A+1,B)\),以此类推。当然等号右边的式子不能包含能一步到位的那一项。移项整理可得:
\[f_i=(f_{i-1}+1)\times m-(f_{s_1}+f_{s_2}+\dots) \]问题变成了如何求后半部分。如果暴力求很可能被卡成 \(O(N^2)\),考虑优化。
我们令上述式子的 \(f_{s_1}+f_{s_2}+\dots=g_{i-1}\),于是可以推出 \(g\) 的含义,即串的某个前缀后面拼上所有可能字符后对应位置的 \(f\) 值之和。既然我们希望的是极长的希望串前缀等于当前串后缀,那么显然 \(g_{i}\) 应该和 \(g_{fail_i}\) 有密切的关系。显然这段开始的那个式子大部分项应该是相同的,除了两个地方。首先根据定义,\(g_i\) 不能包括 \(f_{s_{A_{i+1}}}\),也就是不能包括使得下一个位置刚好符合条件的那一项,减掉即可;而由于 \(g_{fail_i}\) 也没有包括 \(f_{s_{A_{fail_i+1}}}\) 那一项(可能有点绕抱歉),所以要加上这一项。画一下就可以知道两个部分对应的位置,然后正常更新即可。代码异常简略,复杂度 \(O(N)\)。
#include<bits/stdc++.h>
//#define feyn
#define int long long
using namespace std;
const int N=1000010;
const int mod=1e9+7;
inline void read(int &wh){
wh=0;int f=1;char w=getchar();
while(w<'0'||w>'9'){if(w=='-')f=-1;w=getchar();}
while(w<='9'&&w>='0'){wh=wh*10+w-'0';w=getchar();}
wh*=f;return;
}
int m,n,a[N],fail[N];
int f[N],g[N];
signed main(){
#ifdef feyn
freopen("in.txt","r",stdin);
#endif
read(n);read(m);
for(int i=1,j=0;i<=m;i++){
read(a[i]);if(i==1)continue;
while(j&&a[j+1]!=a[i])j=fail[j];
if(a[j+1]==a[i])j++;fail[i]=j;
}
f[1]=n;
for(int i=2;i<=m;i++){
//核心代码
g[i-1]=g[fail[i-1]]+f[fail[i-1]+1]-f[fail[i]];//从fail那里继承,然后加上缺失的部分丢掉不用的部分
f[i]=n*(f[i-1]+1)-g[i-1];//和上面的式子一样更新即可
//核心代码
f[i]%=mod,g[i]%=mod;
}
for(int i=1;i<=m;i++)printf("%lld\n",(f[i]%mod+mod)%mod);
return 0;
}
标签:frac,int,wh,fail,序列,KLAVIR,音符 来源: https://www.cnblogs.com/dai-se-can-tian/p/16543357.html