• 【转载】BZOJ 1009 【HNOI2008】 GT考试


    /*这道题因为有GT,所以就转载了!!!*/

    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取余的结果.

     
      这道题一眼看去像是一道容斥dp,仔细思考后发现其实普通的dp就可以做了。
      我们令fi,jfi,j表示准考证号确定了前i位,其中最后一段已经和不吉利串匹配了jj位的方案数。那么,显然我们只需要枚举每一位选什么数字即可。至于加了一位数字之后最后一段匹配了多少位,完全可以用kmpkmp来解决。因为kmpkmp算法中nextnext数组的含义就是不为整个串的前缀与后缀相等的最大长度。
      但是,这样的复杂度是O(NM2)O(NM2)的。观察发现,MM特别小,于是可以把转移矩阵预处理出来,使用矩阵快速幂优化即可。
      下面贴代码:
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
     
    using namespace std;
    typedef long long llg;
     
    int n,m,k,nt[22],ans;
    char s[22];
    void gi(int &x){if(x>=k) x%=k;}
    struct matrix{
        int w[23][23];
        matrix(){memset(w,0,sizeof(w));}
        void fu(){for(int i=0;i<m;i++) w[i][i]=1;}
        matrix operator * (const matrix &h)const{
            matrix a;
            for(int i=0;i<m;i++)
                for(int j=0;j<m;j++)
                    for(int k=0;k<m;k++)
                        a.w[i][j]+=w[i][k]*h.w[k][j],gi(a.w[i][j]);
            return a;
        }
    }A,Aa;
     
    int getint(){
        int w=0;bool q=0;
        char c=getchar();
        while((c>'9'||c<'0')&&c!='-') c=getchar();
        if(c=='-') c=getchar(),q=1;
        while(c>='0'&&c<='9') w=w*10+c-'0',c=getchar();
        return q?-w:w;
    }
     
    matrix mi(matrix a,int b){
        matrix s; s.fu();
        while(b){
            if(b&1) s=s*a;
            a=a*a; b>>=1;
        }
        return s;
    }
     
    int main(){
        File("a");
        n=getint(); m=getint(); k=getint();
        scanf("%s",s+1);
        for(int i=2,j=0;i<=m;i++){
            while(j && s[j+1]!=s[i]) j=nt[j];
            if(s[j+1]==s[i]) j++;
            nt[i]=j;
        }
        for(int i=0,x;i<m;i++)
            for(int j=0;j<=9;j++){
                x=i;
                while(x && s[x+1]-'0'!=j) x=nt[x];
                if(s[x+1]-'0'==j) x++;
                if(x<m) A.w[i][x]++;
            }
        Aa=mi(A,n);
        for(int i=0;i<m;i++) ans+=Aa.w[0][i],gi(ans);
        printf("%d",ans);
    }
    

      

  • 相关阅读:
    GridView
    母版页
    Ajax完整结构和删除
    Ajax1
    JQuery动画
    JQuery基础
    LinQ高级查询
    C#简单的学籍管理系统(数据库变更由原来的SQLserver改为SqLite)
    C#两个数只能进行+1,-1,*2操作。求使得两个数相等的最小步骤
    C#求最小公倍数与最大公约数
  • 原文地址:https://www.cnblogs.com/YMY666/p/8544919.html
Copyright © 2020-2023  润新知