zoukankan      html  css  js  c++  java
  • 【洛谷4548】[CTSC2006] 歌唱王国(概率生成函数)

    点此看题面

    • 有一个长度为(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;
    }
    
    败得义无反顾,弱得一无是处
  • 相关阅读:
    【转】如何复制一个正在使用的文件?(VB6.0)
    VB6.0操作SQL Server——增删改查
    SQL Server时间戳并发 .
    WCF WinCE 中 手机端 非字符串型 datetime,int,decimal,double 等等 传递不到WCF端的解决方案
    VB中调用带参数存储过程的实现(数据库)
    C# VB6.0 Java C++ GUID 生成
    vb6.0 取得文件扩展名
    VB6.0 取得windows 临时目录 temp
    VB6.0 在代码中直接调用 文件打开对话框,不使用windows控件
    VB数据库记录查询四法
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu4548.html
Copyright © 2011-2022 走看看