- 有一个长度为(n)的文本串和一个初始为空的序列。
- 每次随机产生一个([1,m])的新数加入序列中,求序列中出现给定文本串时的期望长度。
- 数据组数(le50),(n,mle10^5)
概率生成函数
论文题,一个非常诡异的科技,然后这道题是被当作第一道例题拿来讲的。。。
关于概率生成函数可见这篇博客:生成函数学习笔记(三)——概率生成函数初探。
关于(border)
应该是字符串问题,尤其是这类掷骰子问题的一个重要概念。
对于一个字符串(S),若其长为(i)的前缀(S[1,i])和长为(i)的后缀(S[n-i+1,n])相同,则称(S[1,i])为(S)的一个(border)。
具体要求(border),只要(KMP)或者(Hash)即可(个人倾向于后者,简单好懂)。
式子推导
我们设(f_i)表示序列最终长度为(i)的概率,并定义辅助序列(g_i)表示序列长度到达(i)不结束的概率,并另设(a_i)表示(S[1,i])是否为(S)的(border)。
通过分析得到下面两个式子:
[F(x)+G(x)=1+xG(x)\
G(x)cdot(frac1mx)^L=sum_{i=1}^La_icdot F(x)cdot(frac1mx)^{L-i}
]
简单解释一下,对于第一个式子,容易发现(F(x)+G(x))的第(i)项是序列长度能达到(i)的概率,而(xG(x))的第(i)项是序列长度为(i-1)时不结束的概率,显然两者等价,而(1)用于补足常数项。
对于第二个式子,显然给一个未结束的序列后面强行加上给定序列它一定会结束,但不一定加完才会结束,还有可能已添加的序列末尾是给定序列的一个(border),那么直接枚举是哪个(border)即可。
然后我们利用这两个式子来推导一下,考虑期望就是(F'(1)),因此对第一个式子求导一下:
[F'(x)+G'(x)=xG'(x)+G(x)
]
代入(x=1)并移项消去(G'(1))得到:
[F'(1)=G(1)
]
然后我们就想通过第二个式子求出(G(1)),因此把(x=1)代入,注意到其中的(F(1))根据概率生成函数的性质它就等于(1):
[G(1)cdot(frac1m)^L=sum_{i=1}^La_icdot (frac1m)^{L-i}\
G(1)=sum_{i=1}^La_icdot m^i
]
所以说这题最终就被化成了这样一个简单的式子!!!
代码:(O(Tn))
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 100000
#define X 10000
using namespace std;
int n,m;
struct Hash
{
#define ull unsigned long long
#define CU Con ull&
ull x,y;I Hash() {x=y=0;}I Hash(CU a) {x=y=a;}I Hash(CU a,CU b):x(a),y(b){}
I Hash operator + (Con Hash& o) Con {return Hash(x+o.x,y+o.y);}
I Hash operator - (Con Hash& o) Con {return Hash(x-o.x,y-o.y);}
I Hash operator * (Con Hash& o) Con {return Hash(x*o.x,y*o.y);}
I bool operator == (Con Hash& o) Con {return x==o.x&&y==o.y;}
}h[N+5],pw[N+5],sd(324682339,456789001);
int main()
{
RI Tt,i,x,p,t;for(scanf("%d%d",&m,&Tt),pw[0]=i=1;i<=N;++i) pw[i]=pw[i-1]*sd;W(Tt--)
{
for(scanf("%d",&n),i=1;i<=n;++i) scanf("%d",&x),h[i]=h[i-1]+pw[i]*x;//预处理哈希
for(t=0,p=i=1;i<=n;++i) p=1LL*p*m%X,h[i]*pw[n-i]==h[n]-h[n-i]&&(t=(t+p)%X);//枚举每个border计算答案
t<1000&&putchar('0'),t<100&&putchar('0'),t<10&&putchar('0'),printf("%d
",t);//输出四位,不足补0
}return 0;
}