luogu P3193 [HNOI2008]GT考试

传送门

单串匹配显然用(kmp)

一个暴力的dp是设(f_{i,j}),表示前(i)位,正在匹配给定串第(j)位的方案,转移就枚举下一位放什么,然后使用(kmp)看会匹配到给定串的哪位

但是(n)非常大,注意到(f_{i,j}->f_{i+1,k})这样的转移可以抽象为一条从(j)(k)的边,并且(m)很小,于是可以用匹配的关系构建出邻接矩阵,然后矩乘救星了

不会矩乘优化dp的话可以做下这道题

// luogu-judger-enable-o2
#include<bits/stdc++.h>
#define LL long long
#define il inline
#define re register
#define db double
#define eps (1e-7)

using namespace std;
const int N=22;
il LL rd()
{
  re LL x=0,w=1;re char ch=0;
  while(ch<'0'||ch>'9') {if(ch=='-') w=-1;ch=getchar();}
  while(ch>='0'&&ch<='9') {x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
  return x*w;
}
int mod;
struct martix
{
  int n,m;
  int a[N][N];
  martix(){}
  il void init()
  {
    for(int i=0;i<n;i++) a[i][i]=1;
  }
  martix(int n,int m):n(n),m(m)
  {
    for(int i=0;i<n;i++)
      for(int j=0;j<m;j++)
    a[i][j]=0;
  }
  martix operator * (const martix &b) const
  {
    martix a=*this,an(a.n,b.m);
    for(int i=0;i<a.n;i++)
      for(int j=0;j<a.m;j++)
    for(int k=0;k<b.m;k++)
      an.a[i][j]=(an.a[i][j]+a.a[i][k]*b.a[k][j]%mod)%mod;
    return an;
  }
  martix operator ^ (const LL &bb) const
  {
    martix a=*this,an(a.n,a.m);
    an.init();
    LL b=bb;
    while(b)
      {
    if(b&1) an=an*a;
    a=a*a;
    b>>=1;
      }
    return an;
  }
};
char cc[N];
int nxt[N];
int n,m;

int main()
{
  n=rd(),m=rd(),mod=rd();
  martix a(m,m),b(m,m);
  scanf("%s",cc);
  for(int i=1,k=0;i<m;i++)
    {
      while(k&&cc[i]!=cc[k]) k=nxt[k];
      nxt[i+1]=(cc[i]==cc[k])?++k:0;
    }
  for(int i=0;i<m;i++)
    {
      for(int j=0;j<=9;j++)
        {
          int k=i;
          while(k&&cc[k]!=j+'0') k=nxt[k];
          k+=(cc[k]==j+'0');
          ++b.a[i][k];
        }
    }
  a.a[0][0]=1;
  a=a*(b^n);
  int ans=0;
  for(int i=0;i<m;i++) ans=(ans+a.a[0][i])%mod;
  printf("%d
",ans);
  return 0;
}

原文地址:https://www.cnblogs.com/smyjr/p/9739348.html