0
点赞
收藏
分享

微信扫一扫

题解 P3193 【[HNOI2008]GT考试】

三维控件研究 2022-04-21 阅读 41

萌新第一篇题解 希望能有帮助 (本来打算交某谷的 但是写完发现题解已经满了qwq)

首先看到n和m范围巨大的差距就要想到矩阵快速幂

转移方式为直接在自动机上dp

和其他题解一样 这里设g[i][j]表示最长匹配到第i个数字 连接上数字j后的状态

~~蒟蒻语文不好 看不明白可以参考其他题解~~

蒟蒻看到各位大佬求g用的都是kmp 这里提出一种不一样的求法

我们发现g[i][j]的定义和AC自动机的tr[i][j]定义非常像

实际上 g[i][j] 就是一个只有一条链的AC自动机

于是我们可以用建AC自动机的方法求g
当然我们不需要真的建一颗tire出来

直接令第i个数字为第i个状态 令g[i][num[i+1]]=i+1
(num[1-m]为数字串) 然后直接建自动机即可(个人感觉比kmp方便很多)

具体代码:

    for(int i=0;i<m;i++)g[i][num[i+1]]=i+1;
    for(int i=1;i<=m;i++)for(int j=0;j<10;j++)
        if(g[i][j])fail[g[i][j]]=g[fail[i]][j];
        else g[i][j]=g[fail[i]][j];

之后矩阵加速dp即可

关于的矩阵乘法顺序 这里和大多数题解不太一样 看个人喜好

在该顺序下转移矩阵的i,j项表示i状态对j状态贡献的系数

复杂度:O(矩阵快速幂标准复杂度) ~~等我会了markdown可能会来补~~

最后贴上常数极大 码风混乱的代码

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define int long long
#define clr(x) memset(x,0,sizeof(x))
inline int read(){//快读模板
    int x=0;char c=getchar();
    while(c<'0'||c>'9')c=getchar();
    while(c>='0'&&c<='9')x=10*x+c-'0',c=getchar();
    return x;
}
inline int getc(){
    char c=getchar();
    while(c<'0'||c>'9')c=getchar();
    return c-'0';
}
const int maxn=30;
int num[maxn],fail[maxn],g[maxn][10],n,m,p;
struct mix{//蒟蒻的矩阵模板
    int n,m,val[maxn][maxn];
    mix(){clr(val);}
    mix(int a,int b){n=a,m=b;clr(val);}
    mix(int a){n=m=a;clr(val);for(int i=1;i<=a;i++)val[i][i]=1;}//单位矩阵
    int* operator[](int x){return val[x];}
    mix operator*(mix x){
        mix res(n,x.m);
        for(int i=1;i<=n;i++)for(int j=1;j<=x.m;j++)
            for(int k=1;k<=m;k++)res[i][j]=(res[i][j]+val[i][k]*x[k][j]%p)%p;
        return res;
    }
};
mix qpow(mix a,int b){//快速幂
    mix res(a.n);
    for(;b;b>>=1){
        if(b&1)res=res*a;
        a=a*a;
    }
    return res;
}

signed main(){
    n=read(),m=read(),p=read();
    mix a(1,m),b(m,m);a[1][1]=1;//起始状态(其实是0)
    for(int i=1;i<=m;i++)num[i]=getc();
    for(int i=0;i<m;i++)g[i][num[i+1]]=i+1;//建立自动机g[i][j]
    for(int i=1;i<=m;i++)for(int j=0;j<10;j++)
        if(g[i][j])fail[g[i][j]]=g[fail[i]][j];
        else g[i][j]=g[fail[i]][j];
    for(int i=0;i<m;i++)for(int j=0;j<10;j++)if(g[i][j]!=m/*不能转移到m状态*/)b[i+1][g[i][j]+1]++;//由于这里矩阵下标从1开始 所以状态要+1
    int ans=0;a=a*qpow(b,n);//转移
    for(int i=1;i<=m;i++)ans+=a[1][i],ans%=p;//统计答案
    return printf("%lld",ans),0;//输出
}   
举报

相关推荐

0 条评论