• BZOJ1009 矩阵快速幂+DP+KMP


    Problem 1009. -- [HNOI2008]GT考试

    1009: [HNOI2008]GT考试

    Time Limit: 1 Sec  Memory Limit: 162 MB
    Submit: 3773  Solved: 2314
    [Submit][Status][Discuss]

    Description

      阿申准备报名参加GT考试,准考证号为N位数X1X2....Xn(0<=Xi<=9),他不希望准考证号上出现不吉利的数字。
    他的不吉利数学A1A2...Am(0<=Ai<=9)有M位,不出现是指X1X2...Xn中没有恰好一段等于A1A2...Am. A1和X1可以为
    0

    Input

      第一行输入N,M,K.接下来一行输入M位的数。 N<=10^9,M<=20,K<=1000

    Output

      阿申想知道不出现不吉利数字的号码有多少种,输出模K取余的结果.

    Sample Input

    4 3 100
    111

    Sample Output

    81
     
     
     
     
    先给出递推关系式 dp[i][j]=a0*dp[i-1][0]+a1*dp[i-1][1]+a2*dp[i-1][2]+a3*dp[i-1][3]+.......an*dp[i-1][m-1];
     最终有ans=dp[n][0]+dp[n][1]+dp[n][2]+....dp[n][m-1];
    dp[i][j]的意思是前i个数组元素的后缀有j个和所要匹配的字符串相同。
    首先说明这个递推关系式是正确的:将所有合法的号码按   字符串的尾缀与不合法字符串的前缀  相同元素的个数分类,满足不重不漏关系,所以DP是对的。
    然后关系式为线性关系,所以可以用矩阵快速幂来计算。
    剩下的问题就是如何求得a0,a1,a2....an(系数矩阵)。
    进行一遍for循环范围为i=0----m-1,可以知道i只能对i+1之前的元素产生影响,然后再进行填数字。再由KMP进行确定系数。
     
     
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    int n,m,mod;
    int next[25],num[25];
    void get(){
        int i=0,j=-1;
        next[0]=-1;
        while(i<m){
            if(j==-1||num[i]==num[j]) next[++i]=++j;
            else j=next[j];
        }
    }
    struct node{
       int mx[25][25];
       node(){memset(mx,0,sizeof(mx));}
    }a;
    node mult(const node &a,const node &b){
       node c;
       for(int i=0;i<m;++i)
        for(int j=0;j<m;++j)
        for(int k=0;k<m;++k)
        c.mx[i][j]=(c.mx[i][j]+a.mx[i][k]*b.mx[k][j])%mod;
       return c;
    }
    node ksm(node a,int k){
        node r;
        for(int i=0;i<m;++i)
            r.mx[i][i]=1;
        while(k){
            if(k&1) {r=mult(r,a);k|=1;}
            k>>=1;
            a=mult(a,a);
        }
        return r;
    }
    int main(){
       scanf("%d%d%d",&n,&m,&mod);
       getchar();
       for(int i=0;i<m;++i) num[i]=getchar()-'0';
       get();
       for(int i=0;i<m;++i)  //进行第i个元素填充
       for(int j=0;j<=9;++j){ //若第i个元素为j
        int tmp=i;              //这里首先假设后缀满足了i个,然后对i个位置(数组元素从0开始,所以比较的时候还是num[tmp]而不是num[tmp+1])填充j
        while(tmp!=-1&&j!=num[tmp]) tmp=next[tmp];  //若是不相同,就向前找。
        if(tmp==-1) ++a.mx[i][0];   //如果未找到匹配的位置,则dp[i+1][0]的系数a[i][0]要加1
        else ++a.mx[i][tmp+1];  //可以转移到tmp+1的位置(若开始就匹配,就表示可以转移到他的下一个位置,系数加1)
       }//系数矩阵显然是个方阵,第i行第j列表示前一个后缀满足i个转移到后一个后缀满足j个的系数(从而也可以知道系数矩阵第一行起初就是dp[1][0],dp[1][1]...dp[1][m])
       a=ksm(a,n);
       int ans=0;
       for(int i=0;i<m;++i)
        ans=(ans+a.mx[0][i])%mod;
       printf("%d
    ",ans);
    }
     
     
     
  • 相关阅读:
    LoadRunner-参数化(参数间关联)
    LoadRunner-参数化(添加参数值)
    LoadRunner-参数化(连接数据库)
    LoadRunner-录制脚本中文显示乱码
    LoadRunner-循环
    LoadRunner-关联相关(解决方法二)
    LoadRunner-关联报错(解决方法一)
    LoadRunner-关联问题(栏目列表较多关联不了想要的id)
    LoadRunner-关联(自动关联和手动关联)
    Java基础之处理事件——选项按钮的鼠标监听器(Lottery 2 with mouse listener)
  • 原文地址:https://www.cnblogs.com/mfys/p/6898179.html
Copyright © 2020-2023  润新知