bzoj 1009 DP+矩阵加速

我们用DP来解决这个问题

W[I,J]表示准考证的第I位,和不吉利的数匹配到了第J位的方案数,这个状态的表示也可以看成

当前到第I位了,准考证的后J位是不吉利的数的前J位,的方案数

那么我们最后的ans=ΣW[N,I]  0<=I<=M-1

那么我们考虑怎么转移

假设当前到第I位了,匹配到第J位,也就是W[I,J]的值我们有了,我们可以枚举第I+1位是什么,

然后通过KMP的NEXT数组可以快速的得到当前枚举的位可以匹配到第几位,假设可以匹配到第P位,

那么我们W[I+1,P]+=W[I,J],这样就可以转移了

但是我们看N的数据范围是10^9,所以递推是完不成的,这时候需要观察下规律

我们发现转移时的P,J和I是没有关系的,也就是不管I是几,W[I,J]固定会加到W[I+1,K]上

所以我们换一种转移的方式,之前是用W[I,J]更新W[I+1,P],现在我们可以写成

W[I,J]=a0*W[I-1,0]+a1*W[I-1,1]+......+a(m-1)*W[I-1,M-1]

而且ai数组是不变的,那么这个式子就是常系数线性齐次递推式(新买的书上把这个式子叫这个。。),

然后我们可以用矩阵乘法加速,在log级别中求出ans

/**************************************************************
    Problem: 1009
    User: BLADEVIL
    Language: Pascal
    Result: Accepted
    Time:60 ms
    Memory:416 kb
****************************************************************/
 
//By BLADEVIL
type   
    rec                     =array[0..100,0..100] of longint;
     
var
    s                       :ansistring;
    pre                     :array[0..100] of longint;
    n, m, d39               :longint;
    sum, ans                :rec;
    cur                     :longint;
     
procedure init;
var
    i, j, k                 :longint;
    c                       :ansistring;
begin
    readln(n,m,d39);
    readln(s);
    j:=0;
    for i:=2 to m do
    begin
        while (s[i]<>s[j+1]) and (j<>0) do j:=pre[j];
        if s[i]=s[j+1] then
        begin
            inc(j);
            pre[i]:=j;
        end;
    end;
     
    for i:=0 to m-1 do
        for j:=0 to 9 do
        begin
            str(j,c);
            k:=i;
            while (s[k+1]<>c) and (k<>0) do k:=pre[k];
            if s[k+1]=c then inc(k);
            inc(sum[i,k]);
        end;
end;
 
function mul(a,b:rec):rec;
var
    i, j, l                 :longint;
begin
    fillchar(mul,sizeof(mul),0);
    for i:=0 to m-1 do
        for j:=0 to m-1 do
            for l:=0 to m-1 do mul[i,j]:=(mul[i,j]+a[i,l]*b[l,j]) mod d39;
     
end;
 
procedure main;
var
    p                       :longint;
    i                       :longint;
begin
    for i:=0 to m do ans[i,i]:=1;
    p:=n;
    while p<>0 do
    begin
        if p mod 2=1 then ans:=mul(ans,sum);
        p:=p div 2;
        sum:=mul(sum,sum);
    end;
    for i:=0 to m-1 do cur:=(cur+ans[0,i]) mod d39;
    writeln(cur);
end;
 
begin
    init;
    main;
end.

 

posted on 2013-12-20 13:04  BLADEVIL  阅读(1478)  评论(1编辑  收藏  举报