bzoj 1009: [HNOI2008]GT考试(AC自动机+矩阵优化dp)

1009: [HNOI2008]GT考试

Time Limit: 1 Sec   Memory Limit: 162 MB
Submit: 2794   Solved: 1723
[ 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

HINT

Source

[ Submit][ Status][ Discuss]

题解:这道题的暴力dp 与文本生成器一题是一样的。

f[i][j]表示的是原数列的第i位匹配到了trie树的第J个节点。

然后构造优化矩阵,如果当前节点是合法的并且他的儿子也是合法的,那么他就可以推他的儿子,所以在优化矩阵中A[I][CH[I][J]]=1.

然后A^N ,就是转移后的结果。

根据暴力DP我们可以发现,第一步的时候只有与0这个节点相连的节点才会被更新,所以初始矩阵是一个1×tot 的01矩阵。

最后再用初始矩阵,乘以A,将结果累加即为答案。

#include  
#include  
#include    
#include    
#include    
#include    
using namespace std;    
int n,m,p,a[1000],tot;    
int ch[200][26],fail[1003],isend[1000];    
int f[2][1000],cnt;    
int N;    
struct data    
{    
    int a[100][100];    
}e;    
void insert()    
{    
    int now=0;    
    for (int i=1;i<=m;i++)    
     {    
        if (!ch[now][a[i]])   ch[now][a[i]]=++tot;    
        now=ch[now][a[i]];    
     }    
    isend[now]=1;    
}    
void makefail()    
{    
    int now=0;    
    queue p;    
    for (int i=0;i<=9;i++)    
     if (ch[now][i])  p.push(ch[now][i]);    
    while (!p.empty())    
    {    
        int now=p.front(); p.pop();    
        for (int i=0;i<=9;i++)    
         {        
            if (!ch[now][i])    
             {    
                ch[now][i]=ch[fail[now]][i];    
                continue;    
             }    
            int x=ch[now][i];    
            fail[x]=ch[fail[now]][i];    
            if (isend[fail[x]])  isend[x]=1;    
            p.push(x);    
         }    
    }    
}    
void clear(data &x)    
{    
    for (int i=1;i<=N;i++)    
     for (int j=1;j<=N;j++)    
      x.a[i][j]=0;    
}    
void change(data &a,data b)    
{    
    for (int i=1;i<=N;i++)    
     for(int j=1;j<=N;j++)    
      a.a[i][j]=b.a[i][j];    
}    
data mul(data a,data b)    
{    
    data c;    
    for (int i=1;i<=N;i++)    
     for (int j=1;j<=N;j++)    
     {    
        c.a[i][j]=0;    
        for (int k=1;k<=N;k++)    
         c.a[i][j]=(c.a[i][j]+a.a[i][k]*b.a[k][j]%p)%p;    
     }    
    return c;    
}    
data pow(data num,int x)    
{    
    data ans; clear(ans);    
    for (int i=1;i<=N;i++) ans.a[i][i]=1;    
    data base; change(base,num);    
    while(x)    
    {    
        if (x&1)  ans=mul(ans,base);    
        x>>=1;    
        base=mul(base,base);    
    }    
    return ans;    
}    
void build()    
{    
    N=tot; clear(e);    
    for (int i=1;i<=tot;i++)    
     {    
        if (isend[i]) continue;    
        for (int j=0;j<=9;j++)    
         {    
            if (isend[ch[i][j]])  continue;    
            e.a[i][ch[i][j]]=1;    
         }    
     }    
}    
int main()    
{    
    scanf("%d%d%d",&n,&m,&p);    
    char s[30]; scanf("%s",s+1);    
    for (int i=1;i<=m;i++)     
     a[i]=s[i]-'0';    
    insert();      
    for (int i=0;i<=9;i++)  
	if (!ch[0][i])  
	   ch[0][i]=++tot;  
    makefail();    
    build();    
    data ans=pow(e,n-1);    
    for (int i=0;i<=tot;i++)   
      {
      	if (isend[i]) continue;
      	for (int j=0;j<=9;j++)
      	 f[1][ch[i][j]]+=(i==0);
      }
    int t[100]; int sum=0;    
    for (int j=1;j<=N;j++)    
    {    
        t[j]=0;    
        for (int k=1;k<=N;k++)    
         t[j]=(t[j]+f[1][k]*ans.a[k][j]%p)%p;    
        sum=(sum+t[j])%p;    
    }    
    printf("%d\n",sum);    
}    


你可能感兴趣的:(动态规划,字符串处理,矩阵,AC自动机)