「LibreOJ β Round #7」匹配字符串

简述题意

询问有多少长度为 \(n\) 的01串,满足其不存在长度为 \(m\) 的全1子串。

\(n,m\leq 68719476736\)

Solution

\(f_{i}\) 表示长度为 \(i\) 的以 \(0\) 结尾的合法串有多少个,然后枚举不合法的方案。

\[\begin{aligned} f_{n}&=2^{n-1}-\sum^{n-m-1}_{i=0}f_{i}2^{n-i-m-1}\\ F&=1+\frac{x}{1-2x}-\frac{x^{m+1}}{1-2x}F\\ \frac{1-2x+x^{m+1}}{1-2x}F&=\frac{1-x}{1-2x}\\ F&=\frac{1-x}{1-2x+x^{m+1}}\\ &=\frac{(1-x)}{(1-x)-x(1-x^{m})}\\ &=\frac{(1-x)}{(1-x)-x(1-x)\sum^{m-1}_{i=0}x^i}\\ &=\frac{1}{1-\sum^{m}_{i=1}x^{i}}\\ \end{aligned} \]

初始状态为 \(f_{0}=1\),答案就是 \(f_{n+1}\)

那么转移就可以变得比较简洁了:

\[\begin{aligned} f_{n}&=\sum^{m}_{i=1}f_{n-i}\\ f_{n+1}&=\sum^{m}_{i=1}f_{n+1-i}\\ f_{n+1}-f_{n}&=f_{n}-f_{n-m}\\ f_{n}&=2f_{n-1}-f_{n-m-1}\\ \end{aligned} \]

实际上上面这个生成函数很蠢典型的生成函数学傻了,可以直接每次枚举塞在末尾的1的个数,末尾再补一个0,也能直接得到这个。

于是就有了一个组合意义:从 \(0\) 出发,每次可以往右走 \(m+1\) 步,权值乘 \(-1\) ,或者说是向右走 \(1\) 步,权值乘 \(2\),最后的答案就是走到 \(n+1\) 的所有方案的权值之和。

考虑到第一种不会走超过 \(\lfloor\frac{n+1}{m+1}\rfloor\) 次,所以考虑枚举这种转移的次数,可以得到:

\[\begin{aligned} ans&=\sum^{\lfloor\frac{n+1}{m+1}\rfloor}_{i=0}(-1)^{i}2^{n+1-i(m+1)}\binom{n+1-i(m+1)+i}{i}\\ \end{aligned} \]

同时可以发现当 \(m\) 比较小的时候可以使用线性递推,所以总复杂度是 \(O(\min (\frac{n+1}{m+1}\log_{P}n,m\log m \log n))\)

实际上,由于 \(m\) 在小范围内 \(O(m^2 \log n)\) 的做法常数小,也能过,只是要卡卡常。

注意事项

  1. 因为 \(f_{0}=f_{1}=1\) ,所以第二种情况需要特判,我的解决方法是 \(f_{0}=\frac{1}{2}+\frac{1}{2}\),分别统计第一步的走法的两种贡献。
  2. 如果是写 \(O(m^2 \log n)\) 的做法,会导致 \(\lfloor\frac{n+1}{m+1}\rfloor\) 过大,所以需要预处理 \(2\) 的幂次来减小常数。

Code

#include<cstdio>
#include<iostream>
#include<vector>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
#define ll long long 
#define ri int
#define pii pair<int,int>
const int mod=65537;
int add(int x,int y){return (x+=y)<mod?x:x-mod;}
int dec(int x,int y){return (x-=y)<0?x+mod:x;}
int fac[65537],ifac[65537],tmp[65537],base[65537],ans;
int ksm(int d,ll t,int res=1){
    if(t==-1) t=mod-2;t%=(mod-1);
    for(;t;t>>=1,d=1ll*d*d%mod) if(t&1) res=1ll*res*d%mod;return res;
}
ll n,m;
void mul(int *f,int *g){
    for(ri i=0;i<m;++i) for(ri j=0;j<m;++j) tmp[i+j]=add(tmp[i+j],1ll*f[i]*g[j]%mod);
    for(ri i=0;i<m+m;++i) f[i]=tmp[i],tmp[i]=0;
}
void Mod(int *f,int *g){
    for(ri i=m+m;i>=m;--i) if(f[i]){
        int cur=f[i];
        for(ri j=0;j<=m;++j) f[i-j]=dec(f[i-j],1ll*g[m-j]*cur%mod);
    }
}
int f[65537],g[65537],h[65537];
int C(ll x,ll y){
    if(x<y) return 0;
    if(x<mod) return 1ll*fac[x]*ifac[y]%mod*ifac[x-y]%mod;
    return 1ll*C(x/mod,y/mod)*C(x%mod,y%mod)%mod;
}
void solve_xyx(){
    for(ri i=0;i<m;++i) g[i]=mod-1;g[m]=1,h[1]=1,f[0]=1;
    for(;n;n>>=1){
        if(n&1) mul(f,h),Mod(f,g);
        mul(h,h),Mod(h,g);
    }
    for(ri i=0;i<m;++i) ans=add(ans,1ll*(i==0?1:ksm(2,i))*f[i]%mod);
    printf("%d\n",ans);
}
void solve_myy(){
    for(ri i=0;1ll*i*(m+1)<=n+1;++i){
        if(i&1) ans=dec(ans,1ll*ksm(2,n-1ll*i*(m+1))*C(n+1-1ll*i*m,i)%mod);
        else    ans=add(ans,1ll*ksm(2,n-1ll*i*(m+1))*C(n+1-1ll*i*m,i)%mod);
    }
    for(ri i=0;1ll*(i+1)*(m+1)<=n+1;++i){
        if(i&1) ans=add(ans,1ll*ksm(2,n-1ll*(i+1)*(m+1))*C(n+1-1ll*(i+1)*(m+1)+i,i)%mod);
        else    ans=dec(ans,1ll*ksm(2,n-1ll*(i+1)*(m+1))*C(n+1-1ll*(i+1)*(m+1)+i,i)%mod);
    }
    printf("%d\n",ans);
}
int main(){
    base[0]=ifac[0]=fac[0]=1;for(ri i=1;i<mod;++i) base[i]=add(base[i-1],base[i-1]),fac[i]=1ll*fac[i-1]*i%mod,ifac[i]=1ll*ifac[i-1]*ksm(i,mod-2)%mod;
    scanf("%lld%lld",&n,&m);
    if(m==1) return puts("1"),0;
    if(m<=5000) solve_xyx();
    else solve_myy();
}
posted @ 2021-12-19 20:06  krimson  阅读(55)  评论(0编辑  收藏  举报