【luogu P4548】歌唱王国(期望)(生成函数 / 思维)(KMP)

歌唱王国

题目链接: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=1maiF(x)(n1x)mi

然后把第一个式子求导:
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)=(x1)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=1maiF(1)(n1)mi
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=1maiF(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=1maini

不难扩展出当每个字符不一样概率的时候,那每个字符概率是 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=1mai(j=1ipbi)


然后说人类智慧。
这是一个有关鞅(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 bordermai(j=1ipbi)

那赌场要不亏不赚,那进来的钱是多少,是人数,其实也就是字符串长度。
那赌场应该让 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
#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;
}

你可能感兴趣的:(#,生成函数,#,期望,#,字符串,期望,生成函数,思维,KMP,鞅)