1. 程式人生 > >BZOJ 1009--GT考試(KMP&DP&矩陣乘法)

BZOJ 1009--GT考試(KMP&DP&矩陣乘法)

color efi namespace highlight long 情況 ref urn scanf

    RP++

題目鏈接:

    http://www.lydsy.com/JudgeOnline/problem.php?id=1009

Solution

    考慮DP。。。

    dp [ i ] [ j ] 表示現在放完了 i 位,沒有出現不吉利數字,但是末尾已經與不吉利數字最多對應了j 位的情況的數量

    狀態轉移方程。。。說起來很麻煩但很顯然

    先對“不吉利數字”做KMP,然後就可以根據此時狀態進行DP。。

    但是發現 i 這一維太大了。。。不論時間還是空間都是不允許的。。

    於是要用矩陣乘法加速DP。。。

    具體實現還是直接看代碼吧。。。。

代碼

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iostream>
#define M 1010
#define LL long long
#define mod 1000000007
using namespace std;
int n,m,p;
int nxt[M];
char s[25];
struct jz{
    int x[22][22];
    friend jz operator *(const jz &a,const jz &b){
        jz tmp;
        for(int i=0;i<m;i++)
            for(int j=0;j<m;j++){
                tmp.x[i][j]=0;
                for(int k=0;k<m;k++)
                    tmp.x[i][j]=(tmp.x[i][j]+a.x[i][k]*b.x[k][j])%p;
            }
        return tmp;
    }
}a,b;
void KMP(){
    int f=0;
    for(int i=2;i<=m;i++){
        while(f>0&&s[f+1]!=s[i]) f=nxt[f];
        if(s[f+1]==s[i]) f++;
        nxt[i]=f;
    }
    for(int i=0;i<m;i++)
        for(int j=‘0‘;j<=‘9‘;j++){
            f=i;
            while(f>0&&s[f+1]!=j) f=nxt[f];
            if(s[f+1]==j) b.x[i][f+1]++;
            else b.x[i][0]++;
        }
}
void pow(){
    while(n){
        if(n&1) a=a*b;
        n>>=1;
        b=b*b;
    }
}
int main(){
    int ans=0;
    scanf("%d%d%d",&n,&m,&p);
    scanf("%s",s+1);
    KMP();
    for(int i=0;i<m;i++)
        a.x[i][i]=1;
    pow();
    for(int i=0;i<m;i++)
        ans=(ans+a.x[0][i])%p;
    printf("%d\n",ans);
    return 0;
}

  

  

This passage is made by Iscream-2001.

BZOJ 1009--GT考試(KMP&DP&矩陣乘法)