• bzoj1009: [HNOI2008]GT考试


    题解:

    首先看到这道题n特别大,m很小,又有着很明显的状态转移,基本就可以看出这是一道矩阵优化dp题目了(终于不是在看完题解后再说了QAQ)

    可以比较容易的看出状态转移,设f[i][j]表示长度为n的串匹配到了i,长度为m的串匹配到了j,预先处理m串的kmp,找到m串如何转移(我第一次写没有注意到需要处理kmp这回事,直接不符合的话转移到f[i][0]上,这样实际上是漏掉了情况);

    然后构造一个使f[i]转移到f[i+1]的矩阵B,使f[i]*B=f[i+1];

    然后用矩阵快速幂就行了;

    代码:

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<string>
     5 #include<cstdlib>
     6 #include<ctime>
     7 #include<vector>
     8 #include<algorithm>
     9 #include<queue>
    10 #include<map>
    11 #include<vector>
    12 #include<cmath>
    13 using namespace std;
    14 #define LL long long
    15 #define FILE "1"
    16 #define up(i,j,n) for(int i=j;i<=n;i++)
    17 #define down(i,n,j) for(int i=n;i>=j;i--)
    18 int n,m,k;
    19 const int maxn=30;
    20 char s[maxn];
    21 struct M{
    22     int v[maxn][maxn];
    23     M(){memset(v,0,sizeof(v));}
    24     friend M operator*(M a,M b){
    25         M c;
    26         for(int i=0;i<m;i++)
    27             for(int j=0;j<m;j++)
    28                 for(int h=0;h<m;h++)
    29                     c.v[i][j]=(c.v[i][j]+a.v[i][h]*b.v[h][j])%k;
    30         return c;
    31     }
    32     M operator^(int b){
    33         M c;
    34         for(int i=0;i<m;i++)c.v[i][i]=1;
    35         while(b){
    36             if(b%2)c=c*(*this);
    37             b/=2;
    38             *this=*this*(*this);
    39         }
    40         return c;
    41     }
    42 }a,b;
    43 int next[maxn];
    44 int main(){
    45     //freopen("1.in","r",stdin);
    46     scanf("%d%d%d",&n,&m,&k);
    47     scanf("%s",s+1);
    48     int j=0;next[1]=0;
    49     for(int i=2;i<=m;i++){
    50         while(j&&s[j+1]!=s[i])j=next[j];
    51         if(s[j+1]==s[i])j++;
    52         next[i]=j;
    53     }
    54     up(i,0,(m-1))up(j,0,9){
    55         int t=i;
    56         while(t&&s[t+1]-'0'!=j)t=next[t];
    57         if(s[t+1]-'0'==j)t++;
    58         b.v[i][t]=(b.v[i][t]+1)%k;
    59     }
    60     a.v[0][0]=1;
    61     b=b^n;
    62     a=a*b;
    63     int sum=0;
    64     for(int i=0;i<m;i++)sum=(sum+a.v[0][i])%k;
    65     printf("%d
    ",sum);
    66     return 0;
    67 }
    View Code

     //这样的矩阵乘法时间复杂度很高,如果矩阵乘法的内容比较少还是自己写个比较好

  • 相关阅读:
    第13周学习进度情况
    【Android进阶】获取Android软件的版本信息
    【Android进阶】Android程序与JavaScript之间的简单调用
    字符串长度
    约瑟夫问题
    输入n个数和输出调整后的n个数
    输入三个整数,按由小到大的顺序输出
    学校oj平台上不去
    输入10个整数
    输入三个字符串,按由小到大的顺序输出
  • 原文地址:https://www.cnblogs.com/chadinblog/p/5907165.html
Copyright © 2020-2023  润新知