歌唱王国
题目链接:luogu P4548
题目大意
多次询问,每次给你一个字符串,然后有 n 种字符,猴子随机打字。
每个字符打出来的概率相同,然后打出一个串使得给出串是它的子串就停止,问你停止的时候打出来的字符串的期望长度。
思路
首先简单说一下用生成函数的做法:
f
i
f_i
fi 是长度为
i
i
i 结束的概率,
g
i
g_i
gi 是长度为
i
i
i 还没结束的概率。
那一个经典的时候是每个
f
i
f_i
fi 贡献倍率是
i
i
i,那我们要的答案其实就是
F
′
(
1
)
F'(1)
F′(1)
然后列相关的式子。
首先接下来打了一个字符,分成结束了和没有结束。
x
G
(
i
)
+
1
=
F
(
x
)
+
G
(
x
)
xG(i)+1=F(x)+G(x)
xG(i)+1=F(x)+G(x)
那考虑如果你一直按要求加,加
m
m
m 次一定能结束,但是可能在之前就结束了,因为有 border。
于是我们设
o
p
i
op_i
opi 是
[
1
,
i
]
[1,i]
[1,i] 是否是这个串的 border:
G
(
x
)
(
1
n
x
)
m
=
∑
i
=
1
m
a
i
F
(
x
)
(
1
n
x
)
m
−
i
G(x)(\dfrac{1}{n}x)^m=\sum\limits_{i=1}^ma_iF(x)(\dfrac{1}{n}x)^{m-i}
G(x)(n1x)m=i=1∑maiF(x)(n1x)m−i
然后把第一个式子求导:
x
G
′
(
i
)
+
G
(
x
)
=
F
′
(
x
)
+
G
′
(
x
)
xG'(i)+G(x)=F'(x)+G'(x)
xG′(i)+G(x)=F′(x)+G′(x)
F
′
(
x
)
=
(
x
−
1
)
G
′
(
x
)
+
G
(
x
)
F'(x)=(x-1)G'(x)+G(x)
F′(x)=(x−1)G′(x)+G(x)
那我们要的是
F
′
(
1
)
F'(1)
F′(1),直接带入:
F
′
(
1
)
=
G
(
1
)
F'(1)=G(1)
F′(1)=G(1)
那也就是要求
G
(
1
)
G(1)
G(1),那就利用上另一个式子,也带入:
G
(
1
)
(
1
n
)
m
=
∑
i
=
1
m
a
i
F
(
1
)
(
1
n
)
m
−
i
G(1)(\dfrac{1}{n})^m=\sum\limits_{i=1}^ma_iF(1)(\dfrac{1}{n})^{m-i}
G(1)(n1)m=i=1∑maiF(1)(n1)m−i
G
(
1
)
=
∑
i
=
1
m
a
i
F
(
1
)
n
i
G(1)=\sum\limits_{i=1}^ma_iF(1)n^i
G(1)=i=1∑maiF(1)ni
考虑 F ( 1 ) F(1) F(1) 是多少,那思考一下就是每一项的系数加起来,那所有长度结束的概率的和不就是 1 1 1 嘛。
a n s = F ′ ( 1 ) = G ( 1 ) = ∑ i = 1 m a i n i ans=F'(1)=G(1)=\sum\limits_{i=1}^ma_in^i ans=F′(1)=G(1)=i=1∑maini
不难扩展出当每个字符不一样概率的时候,那每个字符概率是
p
i
p_i
pi,给出字符串是
b
i
b_i
bi,那就是:
a
n
s
=
F
′
(
1
)
=
G
(
1
)
=
∑
i
=
1
m
a
i
(
∏
j
=
1
i
p
b
i
)
ans=F'(1)=G(1)=\sum\limits_{i=1}^ma_i(\prod\limits_{j=1}^ip_{b_i})
ans=F′(1)=G(1)=i=1∑mai(j=1∏ipbi)
然后说人类智慧。
这是一个有关鞅(martingale)的方法,不过我们可以把它具象成公平赌博中的博弈。
考虑这么一个赌场,每天选择的字符按顺序是你要求的字符,每天会有一个新人进来,一开始他只有一块钱,他也会按你要求的字符按顺序每天赌,会把全部钱都拿来赌,赔率是
1
:
1:
1:这个字符出现概率的倒数,赌输了就走。
然后赌场把字符放完就让所有人滚。
首先赔率显然没问题,因为要公平。
那接着你注意到是公平博弈,所以赌场要不赚不亏。
那我们可以看赚钱的人总共赚了多少。
那就是:
w = ∑ [ 1 , i ] i s b o r d e r m a i ( ∏ j = 1 i p b i ) w=\sum\limits_{[1,i]is\ border}^ma_i(\prod\limits_{j=1}^ip_{b_i}) w=[1,i]is border∑mai(j=1∏ipbi)
那赌场要不亏不赚,那进来的钱是多少,是人数,其实也就是字符串长度。
那赌场应该让
w
w
w 个人进来,那就是字符串的期望长度就是
w
w
w。
(在鞅的角度大概是你每个时刻每个赌徒的钱数是一个商,然后所有赌徒钱加起来的值
M
i
M_i
Mi 也是鞅)
(然后
T
T
T 是判断要不要停下来的停时
{
T
=
n
}
\{T=n\}
{T=n},然后有个可选停止定理有
E
M
T
=
E
M
0
\mathbb{E}M_T=\mathbb{E}M_0
EMT=EM0)
(那一开始
M
0
=
0
M_0=0
M0=0,所以
E
T
=
M
T
\mathbb{E}T=M_T
ET=MT)
(小孩子不懂事瞎说的)
那实现很简单,直接每次建 KMP,然后跳 fail 即可。
代码
#include<cstdio>
#define mo 10000
using namespace std;
const int N = 1e5 + 100;
int n, t, m, a[N], fail[N], cf[N];
int main() {
scanf("%d %d", &n, &t);
cf[0] = 1; for (int i = 1; i < N; i++) cf[i] = cf[i - 1] * n % mo;
while (t--) {
scanf("%d", &m);
for (int i = 1; i <= m; i++) scanf("%d", &a[i]);
int j = 0;
for (int i = 2; i <= m; i++) {
while (j && a[j + 1] != a[i]) j = fail[j];
if (a[j + 1] == a[i]) j++; fail[i] = j;
}
int ans = 0;
for (int now = m; now; now = fail[now]) (ans += cf[now]) %= mo;
if (ans < 1000) printf("0");
if (ans < 100) printf("0");
if (ans < 10) printf("0");
printf("%d\n", ans);
}
return 0;
}