[HNOI2008] [BZOJ1009] GT考试

    面对这样一道题目,最容易想到的就是动态规划了。首先我们用F[i][j]表示前i个数匹配到第j个的可行方案总数,这里F[i][j]可以从任何满足不吉利数字的第k-j+2位到第k位的不吉利数字全等于前j位的不吉利数字的F[i-1][k]转移过来。那么我们为了得出这样的一个解,我们枚举第i位所加上的数(从0到9),如果能匹配到一个位置,那么每次这个位置就要被匹配到的位置加上。特别注意匹配的j=0和加上同一个数字匹配到多个原答案的情况,这里我们只取最长的。

    因为也在一边写一边学习,稍微借鉴了hzwer的程序。这里我们构造一个答案矩阵,即令

        矩阵A*矩阵B=矩阵C

    其中矩阵A是答案矩阵,矩阵B是上一次的一列答案,共一列,第i个数表示F[x][i]。矩阵C的第i个数则表示F[x+1][i]。我们现在就要构造矩阵A,可以通过之前所说的匹配关系来构造。

    因为矩阵支持结合律的性质,我们利用矩阵快速幂,可以求出最终的答案。附代码:


#include "stdio.h"
#include "iostream"
#include "string.h"
using namespace std;
int n,m,mod,sum=0;
int c[30];
int p[30];
struct matrix{
       int v[30][30];
       int va,vb;
       matrix():va(0),vb(0){memset(v,0,sizeof(v));}
       matrix(int _l,int _r){va=_l,vb=_r;memset(v,0,sizeof(v));}
       void ins(int i,int j,int ins){v[i][j]=(v[i][j]+ins)%mod;}
       void dep(){for(int i=0;i<va;i++) v[i][i]=1;}
};
matrix operator * (matrix a,matrix b){
 matrix c(a.va,b.vb);
 int i,j,k;
 for (i=0; i<a.va; i++){
  for (j=0; j<b.vb; j++){
   for (k=0; k<a.vb; k++){
    c.ins(i,j,a.v[i][k]*b.v[k][j]);
   }
  }
 }
 return c;
}

matrix operator %(matrix q,int c){return q;}

template <class __Type>
void _pow(__Type a,int k,__Type &push){
     while (k){
           if (k%2){
              push=push*a;
              push=push%mod;
           }
           a=a*a;
           a=a%mod;
           k>>=1;
       }
};

int main(){
    scanf("%d%d%d",&n,&m,&mod);
    int i,j;
    for (i=1; i<=m; i++){
        scanf("%1d",c+i);
    }
    for (i=2,j=0; i<=m; i++){
        while (j>0&&c[j+1]!=c[i]) j=p[j];
        if (c[i]==c[j+1])++j;
        p[i]=j;
    }
    matrix sub(m,m); 
    for (i=0; i<m; i++){
        int k;
        for (k=0; k<10; k++){
            j=i;
            while (j>0&&c[j+1]!=k) { j=p[j]; }
            if(c[j+1]==k) j++;
            if(j!=m){
                sub.ins(j,i,1);
            }
        }
    }
    matrix q(m,m);q.dep();
    _pow(sub,n,q);
    long long wh=0;
    for (i=0; i<m; i++){
        wh=(wh+q.v[i][0])%mod;
    }
    cout << wh << endl;
    return 0;
}
 


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值