CTSC2006 歌唱王国
歌唱王国
题解
\(f_i\) 表示 \(i\) 步结束的概率,\(g_i\) 表示 \(i\) 步未结束的概率。
\[g_i=f_{i+1}+g_{i+1}\\
g_i(\frac{1}{m})^L=\sum_{j>i 且 j-i是\text{border}} f_j (\frac{1}{m})^{L-(j-i)}
\]
第一个转移表示掷一次骰子会发生的事情。
第二个转移表示拼一个原串上去会发生的事情。(一定会结束)
https://jkloverdcoi.github.io/2019/12/13/bzoj-1152-歌唱王国/
https://www.cnblogs.com/cjyyb/p/10649150.html
要求的是 \(F’(1)\) ,将 (1) 式两边对 \(x\) 求导后代入 \(x=1\),得到
\[F’(x)+G’(x)=x\cdot G’(x)+G(x)\\
F’(1)=G(1)
\]
将 \(x=1\) 代入 (2) 式,得到
\[G(1)=m^L\sum_{i=1}^n a_i\cdot F(1) \cdot (\frac{1}{m})^{L-i}\\
G(1)=\sum_{i=1}^n a_i\cdot F(1) \cdot m^i\\
\]
注意到 \(F(1)=\sum f_i=1\),所以
\[F’(1)=\sum_{i=1}^n a_i\cdot m^i
\]
于是只需用 KMP 判断给定序列的每个前缀是不是它的 border 就可以了.
用 %04d 可以达到题目要求的输出效果,当然也可以自己写一下输出.
时间复杂度 \(O(n)\)。
CO int N=1e5+10;
int pw[N];
int str[N],nxt[N];
void real_main(){
int n=read<int>();
for(int i=1;i<=n;++i) read(str[i]);
for(int i=2;i<=n;++i){
int j=nxt[i-1];
while(j and str[j+1]!=str[i]) j=nxt[j];
if(str[j+1]==str[i]) ++j;
nxt[i]=j;
}
int ans=0;
for(int i=n;i;i=nxt[i]) ans=add(ans,pw[i]);
printf("%04d\n",ans);
}
int main(){
int m=read<int>();
pw[0]=1;
for(int i=1;i<N;++i) pw[i]=mul(pw[i-1],m);
for(int T=read<int>();T--;) real_main();
return 0;
}
总结概率生成函数:
优点:处理简洁,易扩展,⽐如可以改成求⽅差,或者说求某⼀项的值。
缺点:列⽅程⽐较不直观,需要⼀定的套路积累和练习(+⼀个/+⼀组)。
静渊以有谋,疏通而知事。