【BZOJ】1152: [CTSC2006]歌唱王国Singleland-关于等概率随机串匹配的结论推导

传送门:bzoj1152
下面附赠超良心推导!


这题以前只知道结论,然后代码长这样:

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+50;
const int mod=1e4;
int n,t,len,tp;
int nxt[N],a[N],dp[N];

int main(){
    int i,j,kmp;
    scanf("%d%d",&n,&t);
    while(t--){
        memset(dp,0,sizeof(dp));
        scanf("%d%d",&len,&a[1]);
        dp[1]=n;tp=n;kmp=0;
        for(i=2;i<=len;++i){
            scanf("%d",&a[i]);
            for(;kmp && a[kmp+1]!=a[i];kmp=nxt[kmp]);
            kmp+= a[kmp+1]==a[i]?1:0;
            tp=1ll*tp*n%mod;
            dp[i]=(dp[(nxt[i]=kmp)]+tp)%mod;
        }
        printf("%04d\n",dp[len]);
    }
}

而且总是下意识写成 mod=1e5 m o d = 1 e 5 ,导致每次交都是先WA一次。。。
现在膜了 VFK V F K 神犇的博客终于会推导了!然后用下面这份代码卡到洛谷rk1,bzoj rk4(然而ccosi并没有高超的卡常技巧)

代码如下

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+100,mod=1e4;

int n,m,que,nxt[N],pw[N],ans;
int a[N];

inline int ad(int x,int y){x+=y;if(x>=mod) x-=mod;return x;}

inline char gc(){
    static char now[1<<16],*S,*T;
    if (T==S){T=(S=now)+fread(now,1,1<<16,stdin);if (T==S) return EOF;}
    return *S++;
}

char c;
inline void rd(int &x)
{
    c=gc();x=0;
    for(;!isdigit(c);c=gc());
    for(;isdigit(c);c=gc()) x=x*10+(c^48);
}

int main(){
    register int i;int kmp,lim=0;pw[0]=1;
    rd(n);rd(que);
    for(;que;--que){
        rd(m);
        if(m>lim) {
          for(i=lim+1;i<=m;++i) pw[i]=1ll*pw[i-1]*n%mod;
          lim=m;
        } 
        for(i=1;i<=m;++i) rd(a[i]);
        nxt[1]=kmp=0;
        for(i=2;i<=m;++i){
            for(;a[kmp+1]!=a[i] && kmp;kmp=nxt[kmp]);
            nxt[i]=(kmp+= (a[kmp+1]==a[i]));
        }
        for(ans=0,i=m;i;i=nxt[i]) ans=ad(ans,pw[i]);
        printf("%04d\n",ans);
    }
}

推导

首先声明推导方法均仿照 VFK V F K 神犇,仅仅是记录下来供自己学习掌握,而且 LaTeX L a T e X 渲染的公式看着也顺眼许多…

符号说明:

  • s s 代表目标数字串,ans代表平均歌唱时间。
  • 用任意小写字母(如 a a )来表示某一数字串,也可以表示由单个数字构成的数字串;用任意大写字母(如A)来表示满足某一条件的数字串的集合。
  • |a| | a | 表示数字串 a a 的长度。
  • ab表示数字串 b b 接在数字串a后所构成的数字串,也即数字串 a,b a , b 串联后的字符串。
  • AB A ∪ B 表示数字串集合 A A 和数字串集合B的并集。
  • AB A ∩ B 表示数字串集合 A A 和数字串集合B的交集。
  • ϕ ϕ 表示空串(没有找到空集的符号代码,就用欧拉函数的代替一下)。

给定集合:

  • 设数字串集合 Q Q Q={a|assaϕ}
  • 设数字串集合 P P ​ P={a|as,aϕ,as,sabQ} P = { a | a 是 s 的 后 缀 , a ≠ ϕ , a ≠ s , s 删 除 a 这 个 后 缀 得 到 的 前 缀 子 串 b ∈ Q } ​
  • 设数字串集合 Y Y Y={a|sasa}
  • 设数字串集合 N N N={a|saas}

直接得到 ans=aY|a|(1n)|a| a n s = ∑ a ∈ Y | a | ( 1 n ) | a |

于是想到可以设 f(A)=aA|a|(1n)|a| f ( A ) = ∑ a ∈ A | a | ( 1 n ) | a | 来化简式子求得 f(Y) f ( Y ) 。但是 |a| | a | f(A) f ( A ) 中出现了两次不是很能方便地化简。

于是设 g(A,z)=aA(zn)|a| g ( A , z ) = ∑ a ∈ A ( z n ) | a | ,对 g(A,z) g ( A , z ) z z 求偏导得g(A,z)=aA|a|(1n)|a|z|a|1

那么 ans=f(Y)=g(Y,1) a n s = f ( Y ) = g ′ ( Y , 1 ) 。可以先求得 g(A,z)=aA(zn)|a| g ( A , z ) = ∑ a ∈ A ( z n ) | a | ,再对其求 z z 的偏导。

现在的任务就是找一些关于Y的等式,来求出 g(Y,1) g ( Y , 1 ) 的一个仅关于 P P 的化简式。所以观察一下P,Y,N之间的关系:

ϕ+aNni=1ai=Y+N ϕ + ∑ a ∈ N ∑ i = 1 n a i = Y + N

aNas=Y+aYpPap ∑ a ∈ N a s = Y + ∑ a ∈ Y ∑ p ∈ P a p

接着列出 g(A,z) g ( A , z ) 的一些计算:

  • AB=ϕ A ∩ B = ϕ g(AB,z)=g(A,z)+g(B,z) g ( A ∪ B , z ) = g ( A , z ) + g ( B , z )
  • g(aAab,z)=(zn)|b|g(A,z) g ( ∑ a ∈ A a b , z ) = ( z n ) | b | g ( A , z )
  • g(aAbBab,z)=g(A,z)g(B,z) g ( ∑ a ∈ A ∑ b ∈ B a b , z ) = g ( A , z ) g ( B , z )

将上面两个等式转换成 g(A,z) g ( A , z ) 的形式:

g(ϕ,z)+(ni=1zn)g(N,z)=g(Y,z)+g(N,z) g ( ϕ , z ) + ( ∑ i = 1 n z n ) g ( N , z ) = g ( Y , z ) + g ( N , z )

(zn)|s|g(N,z)=g(Y,z)+g(Y,z)g(P,z) ( z n ) | s | g ( N , z ) = g ( Y , z ) + g ( Y , z ) g ( P , z )

稍微化一化:

1+zg(N,z)=g(Y,z)+g(N,z) 1 + z g ( N , z ) = g ( Y , z ) + g ( N , z )

(zn)|s|g(N,z)=(1+g(P,z))g(Y,z) ( z n ) | s | g ( N , z ) = ( 1 + g ( P , z ) ) g ( Y , z )

大力解方程,用 g(P,z) g ( P , z ) 表示 g(Y,z) g ( Y , z ) :

g(Y,z)=(zn)|s|g(P,z)zg(P,z)z+1+(zn)|s| g ( Y , z ) = ( z n ) | s | g ( P , z ) − z g ( P , z ) − z + 1 + ( z n ) | s |

式子项比较多,按设分子为 h(z) h ( z ) ,分母为 q(z) q ( z ) 分别来求关于 z z 的偏导化解:

h(z)=(zn)|s|,h(z)=|s|(1n)|s|z|s|1

q(z)=aP(zn)|a|aPz|a|+1(1n)|a|z+1+(zn)|s| q ( z ) = ∑ a ∈ P ( z n ) | a | − ∑ a ∈ P z | a | + 1 ( 1 n ) | a | − z + 1 + ( z n ) | s |

q(z)=aP|a|(1n)|a|z|a|1aP(|a|+1)(1n)|a|z|a|1+0+|s|(1n)sz|s|1 q ′ ( z ) = ∑ a ∈ P | a | ( 1 n ) | a | z | a | − 1 − ∑ a ∈ P ( | a | + 1 ) ( 1 n ) | a | z | a | − 1 + 0 + | s | ( 1 n ) s z | s | − 1

z=1 z = 1 ,得:

h(1)=(1n)|s|,h(1)=|s|(1n)|s| h ( 1 ) = ( 1 n ) | s | , h ′ ( 1 ) = | s | ( 1 n ) | s |

q(1)=(1n)|s|,q(1)=|s|(1n)|s|aP(1n)|a|1 q ( 1 ) = ( 1 n ) | s | , q ′ ( 1 ) = | s | ( 1 n ) | s | − ∑ a ∈ P ( 1 n ) | a | − 1

带回原式:

g(Y,1)=h(1)q(1)h(1)q(1)q(1)q(1) g ′ ( Y , 1 ) = h ′ ( 1 ) q ( 1 ) − h ( 1 ) q ′ ( 1 ) q ( 1 ) q ( 1 )

大力带入计算,奇妙的事情发生了,得到这样的一个化简式:

g(Y,1)=aP(1n)|a|+1(1n)|s|=aPn|s||a|+n|s| g ′ ( Y , 1 ) = ∑ a ∈ P ( 1 n ) | a | + 1 ( 1 n ) | s | = ∑ a ∈ P n | s | − | a | + n | s |

由前面定义的数字串集合 P P 的性质发现|s||a|实际上枚举到了 Q Q s以外的所有数字串的长度,而 s s 也在另一项n|s|中补上了,所以最终:

ans=f(Y)=g(Y,1)=aQn|a| a n s = f ( Y ) = g ′ ( Y , 1 ) = ∑ a ∈ Q n | a |

数字串集合 Q Q s做一遍 kmp k m p 即可求得,所以除开预处理 n n 的次幂,复杂度是O(n)的。

话说建立 kmp k m p next n e x t 数组的复杂度怎么证啊。这里是一个自己脑补的证明:

建立过程中每次加入一个字符, kmp k m p 最多后移一位,每次匹配失败,跳 next n e x t 链至少会前移一位(除非已经到了初始位置)。设总共了 x x next链,字符串长度为 n n ,则nextnnx。由 0nextn<n 0 ≤ n e x t n < n xn x ≤ n ,建立总复杂度 x+n2n x + n ≤ 2 n ,没有仔细想,但上界应该很难卡满。

代码按式子代入计算即可。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值