[BZOJ1152][CTSC2006]歌唱王国Singleland(KMP + 概率生成函数)

Address

Solution

  • 看了 yml 大佬的 IOI2018 国家候选队论文,第一次了解到概率生成函数
  • orz YML
  • 先介绍一下概率生成函数
  • 一个离散型随机变量 X X X 的概率生成函数为:
  • F ( x ) = ∑ i ≥ 0 P ( X = i ) x i F(x)=\sum_{i\ge0}P(X=i)x^i F(x)=i0P(X=i)xi
  • 显然有 F ( 1 ) = 1 F(1)=1 F(1)=1
  • 概率生成函数的应用之一就是如果按照上面的定义,则 X X X 的期望为
  • F ′ ( 1 ) F'(1) F(1)
  • 方差为
  • F ′ ′ ( 1 ) + F ′ ( 1 ) − ( F ′ ( 1 ) ) 2 F''(1)+F'(1)-(F'(1))^2 F(1)+F(1)(F(1))2
  • 回到原问题,设随机变量 X X X 表示插入多少个字符之后匹配到模式串,其概率生成函数为 F ( x ) F(x) F(x)
  • g i g_i gi 表示插入 i i i 个字符之后没有匹配到模式串的概率,其一般生成函数 G ( x ) G(x) G(x)
  • 可以推出两点性质
  • F ( x ) + G ( x ) = x G ( x ) + 1 F(x)+G(x)=xG(x)+1 F(x)+G(x)=xG(x)+1
  • 解释: x G ( x ) + 1 xG(x)+1 xG(x)+1 的第 i i i i > 0 i>0 i>0 )项实际上是表示插入 i − 1 i-1 i1 个字符后没有匹配成功的概率
  • 而插入 i − 1 i-1 i1 个字符后没有匹配成功,就相当于插入 i i i 个字符之后匹配成功或失败
  • F ( x ) + G ( x ) F(x)+G(x) F(x)+G(x) 的第 i i i i > 0 i>0 i>0 )项正好对应
  • F ( x ) F(x) F(x) 的第 0 0 0 项为 0 0 0 G ( x ) G(x) G(x) 的第 0 0 0 项为 1 1 1
  • G ( x ) × ( 1 n x ) m = ∑ i = 1 m a i × F ( x ) × ( 1 n x ) m − i G(x)\times(\frac 1nx)^m=\sum_{i=1}^ma_i\times F(x)\times (\frac 1nx)^{m-i} G(x)×(n1x)m=i=1mai×F(x)×(n1x)mi
  • 上面 n n n 表示字符集大小, m m m 表示模式串长度, a i a_i ai 表示模式串的长度为 i i i 的前缀和长度为 i i i 的后缀是否相等(可用 KMP 求出),相等则为 1 1 1 ,不相等则为 0 0 0
  • 让我们来解释下这个看上去没有实际含义的式子
  • 如果我们现在插入了一些字符,但没有匹配成功
  • 那么如果把长度为 m m m 的模式串直接追加在我们已经插入的字符后面,就一定能匹配成功,这对应了等号左边。等号左边多项式 i + m i+m i+m 次项的意义可以看作匹配成功后不结束操作,但需要满足在插入 i i i 个字符时不能匹配成功,在插入 i + m i+m i+m 个字符时恰好长度为 m m m 的后缀与模式串匹配成功的概率
  • 分析一下等号右边 i + m i+m i+m 次项的含义
  • ∑ j = 1 m a j P ( X = i + j ) ( 1 n ) m − j \sum_{j=1}^ma_jP(X=i+j)(\frac1n)^{m-j} j=1majP(X=i+j)(n1)mj
  • 枚举 j j j 就相当于枚举第一次匹配成功时已经插入了 i + j i+j i+j 个字符
  • 然后还要再插入 m − j m-j mj 个字符,使得当插入的总字符数达到 i + m i+m i+m 时再次匹配成功
  • 这时候就必须满足模式串的长度为 j j j 的前缀和长度为长度为 j j j 的后缀相等
  • ( 1 n ) m − j (\frac 1n)^{m-j} (n1)mj 就表示接下来插入的 m − j m-j mj 个字符必须是模式串的后缀
  • 解释完毕
  • F ( x ) + G ( x ) = x G ( x ) + 1 F(x)+G(x)=xG(x)+1 F(x)+G(x)=xG(x)+1 两边同时求导
  • F ′ ( x ) + G ′ ( x ) = x G ′ ( x ) + G ( x ) F'(x)+G'(x)=xG'(x)+G(x) F(x)+G(x)=xG(x)+G(x)
  • 现在我们要求 F ′ ( 1 ) F'(1) F(1) ,由上面得出:
  • F ′ ( 1 ) = G ( 1 ) F'(1)=G(1) F(1)=G(1)
  • 于是
  • F ′ ( 1 ) = n m ∑ i = 1 m a i × F ( 1 ) × ( 1 n ) m − i F'(1)=n^m\sum_{i=1}^ma_i\times F(1)\times(\frac 1n)^{m-i} F(1)=nmi=1mai×F(1)×(n1)mi
  • = ∑ i = 1 m a i × n i =\sum_{i=1}^ma_i\times n^i =i=1mai×ni
  • 所以答案为
  • ∑ i = 1 m a i × n i \sum_{i=1}^ma_i\times n^i i=1mai×ni
  • 于是我们就做完了 ,是不是很简单

Code

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define For(i, a, b) for (i = a; i <= b; i++)

inline int read()
{
	int res = 0; bool bo = 0; char c;
	while (((c = getchar()) < '0' || c > '9') && c != '-');
	if (c == '-') bo = 1; else res = c - 48;
	while ((c = getchar()) >= '0' && c <= '9')
		res = (res << 3) + (res << 1) + (c - 48);
	return bo ? ~res + 1 : res;
}

const int N = 1e5 + 5, ZZQ = 1e4;

int n, T, m, pw[N], s[N], nxt[N];

void work()
{
	int i, j = 0, ans = 0;
	m = read();
	For (i, 1, m) s[i] = read();
	For (i, 2, m)
	{
		while (j && s[j + 1] != s[i]) j = nxt[j];
		if (s[j + 1] == s[i]) j++;
		nxt[i] = j;
	}
	i = m;
	while (i) ans = (ans + pw[i]) % ZZQ, i = nxt[i];
	printf("%04d\n", ans);
}

int main()
{
	int i;
	n = read(); T = read();
	pw[0] = 1;
	For (i, 1, 100000) pw[i] = pw[i - 1] * n % ZZQ;
	while (T--) work();
	return 0;
}
©️2020 CSDN 皮肤主题: 创作都市 设计师:CSDN官方博客 返回首页