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

版权声明:转载请附带链接或评论 https://blog.csdn.net/corsica6/article/details/81739236

传送门: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,导致每次交都是先WA一次。。。
现在膜了VFK神犇的博客终于会推导了!然后用下面这份代码卡到洛谷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神犇,仅仅是记录下来供自己学习掌握,而且LaTeX渲染的公式看着也顺眼许多…

符号说明:

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

给定集合:

  • 设数字串集合QQ={a|assaϕ}
  • 设数字串集合PP={a|as,aϕ,as,sabQ}
  • 设数字串集合YY={a|sasa}
  • 设数字串集合NN={a|saas}

直接得到ans=aY|a|(1n)|a|

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

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

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

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

ϕ+aNi=1nai=Y+N

aNas=Y+aYpPap

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

  • AB=ϕg(AB,z)=g(A,z)+g(B,z)
  • g(aAab,z)=(zn)|b|g(A,z)
  • g(aAbBab,z)=g(A,z)g(B,z)

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

g(ϕ,z)+(i=1nzn)g(N,z)=g(Y,z)+g(N,z)

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

稍微化一化:

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

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

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

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

式子项比较多,按设分子为h(z),分母为q(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)=aP|a|(1n)|a|z|a|1aP(|a|+1)(1n)|a|z|a|1+0+|s|(1n)sz|s|1

z=1,得:

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

q(1)=(1n)|s|,q(1)=|s|(1n)|s|aP(1n)|a|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|

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

ans=f(Y)=g(Y,1)=aQn|a|

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

话说建立kmpnext数组的复杂度怎么证啊。这里是一个自己脑补的证明:

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

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

阅读更多 登录后自动展开
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页