歌唱王国
题解
(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是 ext{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)=xcdot G’(x)+G(x)\
F’(1)=G(1)
]
将 (x=1) 代入 (2) 式,得到
[G(1)=m^Lsum_{i=1}^n a_icdot F(1) cdot (frac{1}{m})^{L-i}\
G(1)=sum_{i=1}^n a_icdot F(1) cdot m^i\
]
注意到 (F(1)=sum f_i=1),所以
[F’(1)=sum_{i=1}^n a_icdot 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
",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;
}
总结概率生成函数:
优点:处理简洁,易扩展,⽐如可以改成求⽅差,或者说求某⼀项的值。
缺点:列⽅程⽐较不直观,需要⼀定的套路积累和练习(+⼀个/+⼀组)。