bzoj 1009: [HNOI2008]GT考试 KMP+矩阵乘法

来源:互联网 发布:网络怎么连接的 编辑:程序博客网 时间:2024/06/08 15:53

题意

求有多少个长度为n且仅包含0到9的字符串不包含一个长度为m的子序列。
n<=10^9,m<=20

分析

先得到s的next数组,然后根据dp方程f[n][u]+=f[n-1][i],表示在kmp上走了n步走到节点u的方案数,我们可以构造一个状态转移矩阵,然后矩阵快速幂就好了。

代码

#include<iostream>#include<cstdio>#include<cstdlib>#include<cstring>#include<algorithm>#define ll long long#define N 25using namespace std;int n,m,MOD,next[N];char s[N];struct arr{int a[N][N];}a,b,c;int getnext(int n){    next[0]=-1;next[1]=0;    int i=2,j=0;    while (i<=n)        if (s[j+1]==s[i]||j==-1)        {            j++;            next[i]=j;            i++;        }        else j=next[j];}void mul(arr a,arr b){    memset(c.a,0,sizeof(c.a));    for (int i=0;i<=m;i++)        for (int j=0;j<=m;j++)            for (int k=0;k<=m;k++)                c.a[i][j]=(c.a[i][j]+(ll)a.a[i][k]*b.a[k][j]%MOD)%MOD;}void ksm(int x){    if (x==1) return;    ksm(x/2);    mul(c,c);    if (x%2==1) mul(c,a);}int main(){    scanf("%d%d%d",&n,&m,&MOD);    scanf("%s",s+1);    getnext(m);    for (int i=0;i<=m;i++)        for (int j=0;j<=9;j++)        {            int u=i;            while (u&&s[u+1]-'0'!=j) u=next[u];            if (s[u+1]-'0'==j) u++;            if (u==m) continue;            //f[n][u]+=f[n-1][i]            a.a[i][u]++;        }    c=a;    ksm(n);    int ans=0;    for (int i=0;i<m;i++)        ans=(ans+c.a[0][i])%MOD;    printf("%d",ans);    return 0;}
0 0