【洛谷 P4548 [CTSC2006]歌唱王国】【概率生成函数+KMP】

题意

给一个长度为 n n n 的序列 A A A。有一个空序列 B B B,每次会等概率随机往该序列的末尾加入 1 1 1 m m m 中的一个整数,若 A A A 成为了 B B B 的子串,则停止。求序列 B B B 的期望长度。
n ≤ 1 0 5 n\le 10^5 n105

分析

定义一个离散随机变量 X X X 的概率生成函数为 F ( z ) = ∑ i ≥ 0 P ( X = i ) z i F(z)=\sum_{i\ge 0}P(X = i)z^i F(z)=i0P(X=i)zi

其中 P ( X = i ) P(X=i) P(X=i) 表示 X X X 取值为 i i i 的概率。不难得到 F ( 1 ) = 1 , F ′ ( 1 ) = E ( X ) F(1)=1,F'(1)=E(X) F(1)=1,F(1)=E(X)

回到题目,设 a i a_i ai 表示 A A A 长度为 i i i 的前缀是否是 A A A 的一个 border,是则为 1 1 1,否则为 0 0 0。设 f i f_i fi 表示结束时 B B B 的长度恰好为 i i i 的概率,其概率生成函数为 F ( x ) F(x) F(x) g i g_i gi 表示 B B B 的长度为 i i i 时还没结束的概率,生成函数为 G ( x ) G(x) G(x)

则有等式 F ( x ) + G ( x ) = 1 + x G ( x ) (1) F(x)+G(x)=1+xG(x)\tag{1} F(x)+G(x)=1+xG(x)(1)

G ( x ) ( 1 m x ) n = ∑ i = 1 n a i F ( x ) ( 1 m x ) n − i (2) G(x)(\frac{1}{m}x)^n=\sum_{i=1}^na_iF(x)(\frac{1}{m}x)^{n-i}\tag{2} G(x)(m1x)n=i=1naiF(x)(m1x)ni(2)

( 1 ) (1) (1) 式可以理解为,在序列末尾随机加入一个数字后,有可能结束,也有可能不结束。

( 2 ) (2) (2) 式可以理解为,在一个还未结束的串后面加入原串,则一定会结束。但可能还没全部加入就已经结束。右边的 i i i 枚举的是结束后还没被加入部分的长度,显然这部分一定是原串的 border。

( 1 ) (1) (1) 式求导并代入 x = 1 x=1 x=1,可得 F ′ ( x ) + G ′ ( x ) = G ( x ) + x G ′ ( x ) F'(x)+G'(x)=G(x)+xG'(x) F(x)+G(x)=G(x)+xG(x)

F ′ ( 1 ) = G ( 1 ) F'(1)=G(1) F(1)=G(1)

( 2 ) (2) (2) 式代入 x = 1 x=1 x=1 可得 G ( 1 ) = ∑ i = 1 n a i m i F ( 1 ) = ∑ i = 1 n a i m i G(1)=\sum_{i=1}^na_im^iF(1)=\sum_{i=1}^na_im^i G(1)=i=1naimiF(1)=i=1naimi

用 KMP 求出 a i a_i ai 即可。时间复杂度 O ( n ) O(n) O(n)

代码

#include
using namespace std;

const int N = 100005;
const int mo = 1e4;

int n, t, a[N], nx[N], m;
bool vis[N];

void get_next(int n)
{
	nx[0] = -1; nx[1] = 0;
	int i = 2, j = 0;
	while (i <= n)
		if (j == -1 || a[j + 1] == a[i])
		{
			nx[i] = ++j;
			i++;
		}
		else j = nx[j];
}

void pri(int x)
{
	vector<int> vec;
	while (x) vec.push_back(x % 10), x /= 10;
	while (vec.size() < 4) vec.push_back(0);
	for (int i = 3; i >= 0; i--) putchar(vec[i] + '0');
	puts("");
}

int main()
{
	scanf("%d%d", &n, &t);
	while (t--)
	{
		scanf("%d", &m);
		for (int i = 1; i <= m; i++) scanf("%d", &a[i]), vis[i] = 0;
		get_next(m);
		int ans = 0, k = m;
		while (k) vis[k] = 1, k = nx[k];
		for (int i = 1, w = n % mo; i <= m; i++, w = w * n % mo) (ans += vis[i] * w) %= mo;
		pri(ans);
	}
	return 0;
}

你可能感兴趣的:(KMP算法,生成函数)