BZOJ 1009 HNOI2008 GT考试 KMP+dp+矩阵快速幂

题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=1009

题意概述:

  求有多少种方案构造一个长度为N的数字串,使得其中不包含给出的长度为M的数字串(数字串可以包含前导0),答案mod K。

  N<=10^9,M<=20,K<=1000.

分析:

  首先看到这个东西就想到了字符串+dp的套路......注意到N非常**大(滑稽)可能可以用矩阵快速幂优化一下。

  那么构造一个线性的dp方程。

  因为只有一个串,那么考虑用KMP来辅助dp。

  令f(i,j)表示已经完成构造第i个字符,当前构造字符串和给出的串最长公共后缀前缀长度为j的方案数。

  先考虑j!=0,有一种转移方案是f(i-1,j-1),另外有转移方案为f(i-1,k),满足S[j-1]是k的fail路径上第一个等于s[j-1]的数字。(因为公共前后缀长度为j的情况下当前字符是唯一确定的,所以所有的转移前面的系数为1)

  然后考虑j=0,有一种转移是f(i-1,0)*9,另外有转移f(i-1,k),前面的系数为k到0的fail路径上没有出现过的数字的数量。

  请仔细体会这奥妙无穷的dp......(我要不要这么蠢啊...都不仔细想想...一遍就构造好的话哪里来的这么多事情)

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<cstdlib>
 5 #include<algorithm>
 6 #include<cmath>
 7 #include<queue>
 8 #include<set>
 9 #include<map>
10 #include<vector>
11 #include<cctype>
12 using namespace std;
13 const int maxm=25;
14 
15 int N,M,K,f[maxm];
16 char S[maxm];
17 struct matrix{
18     static const int maxn=22;
19     int a[maxn][maxn],r,c;
20     matrix(){ memset(a,0,sizeof(a)); r=c=0; }
21     void init(int len){
22         r=c=len;
23         for(int i=0;i<r;i++) a[i][i]=1;
24     }
25     friend matrix operator * (matrix x,matrix y){
26         matrix z;
27         z.r=x.r,z.c=y.c;
28         for(int i=0;i<z.r;i++)
29         for(int j=0;j<z.c;j++)
30         for(int k=0;k<x.c;k++)
31             z.a[i][j]=(z.a[i][j]+x.a[i][k]*y.a[k][j])%K;
32         return z;
33     }
34     matrix qkpow(int y){
35         matrix re,t;
36         re.init(this->r); t=*this;
37         for(int i=0;(1<<i)<=y;i++){
38             if((1<<i)&y) re=re*t;
39             t=t*t;
40         }
41         return re;
42     }
43 }A,B;
44 
45 void getfail(char *t)
46 {
47     f[0]=f[1]=0;
48     int n=strlen(t);
49     for(int i=1;i<n;i++){
50         int j=f[i];
51         while(j&&t[i]!=t[j]) j=f[j];
52         f[i+1]=t[i]==t[j]?j+1:0;
53     }
54 }
55 void work()
56 {
57     scanf("%d%d%d%s",&N,&M,&K,&S);
58     getfail(S);
59     A.r=A.c=M;
60     A.a[0][0]=9;
61     bool vis[10];
62     for(int i=1;i<M;i++){
63         memset(vis,0,sizeof(vis));
64         int p=i;
65         while(p) vis[S[p]-'0']=1,p=f[p];
66         vis[S[0]-'0']=1;
67         for(int k=0;k<10;k++)
68             if(!vis[k]) A.a[i][0]++;
69     }
70     for(int j=1;j<M;j++){
71         A.a[j-1][j]=1;
72         for(int i=j;i<M;i++){
73             int p=i;
74             while(p>j-1){
75                 if(S[p]==S[j-1]) break;
76                 p=f[p];
77             }
78             if(p==j-1) A.a[i][j]=1;
79         }
80     }
81     B.r=1,B.c=M;
82     B.a[0][0]=9,B.a[0][1]=1;
83     B=B*A.qkpow(N-1);
84     int ans=0;
85     for(int j=0;j<M;j++) ans=(ans+B.a[0][j])%K;
86     printf("%d
",ans);
87 }
88 int main()
89 {
90     work();
91     return 0;
92 }
View Code
原文地址:https://www.cnblogs.com/KKKorange/p/8743933.html