[HNOI2008]GT考试

本文介绍了一种使用KMP算法和矩阵乘法解决准考证号码生成问题的方法,确保号码中不包含不吉利数字。通过动态规划(DP)实现算法优化,最终输出满足条件的号码数量,模K取余的结果。

Time Limit: 1 Sec  Memory Limit: 162 M

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位的数。 100%数据N<=10^9,M<=20,K<=1000 40%数据N<=1000 10%数据N<=6

Output

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

Sample Input

4 3 100
111

Sample Output

81
 
 
题解:DP(推出转移方程后就是KMP+矩阵乘法了)
f[i,j]表示到第i位匹配了不吉利数字的多少位。
转移就是从i-1转移,考虑每一个f[i,k]与当前位0~9的组合可以匹配不吉利数字多少位。
最后输出sigma(f[n,k](0<=k<=m-1))。
在转移中如果枚举每一种情况,就会编得很蛋疼。。可是观察发现,这可以看成是一种字符串匹配,所以考虑用KMP。(关于KMP参见Matrix67的blog,查KMP即可)
然后发现,对于每一个i和i-1的转移方程都是相同的,就可以用矩阵乘法优化(关于矩阵优化。。。介个嘛)。
 
AC CODE
 
program hy_1009;
var T,tt,tmp:array[0..20,0..20] of longint;
    a,p,s,f:array[0..20] of longint;
    n,m,kk:longint;
//============================================================================
procedure init;
var i:longint;
    ch:char;
begin
  readln(n,m,kk);
  for i:=1 to m do
  begin
    read(ch);
    a[i]:=ord(ch)-ord('0');
  end;
end;
//============================================================================
procedure KMP;
var i,x:longint;
begin
  p[0]:=-1;
  for i:=2 to m do
  begin
    x:=p[i-1];
    repeat
      if a[x+1]=a[i] then
      begin
        p[i]:=x+1; break;
      end x:=p[x];
    until x=-1;
  end;
end;
//============================================================================
procedure Set_Matrix;
var i,j,x:longint;
begin
  for i:=0 to m-1 do
    for j:=0 to 9 do
    begin
      x:=i;
      repeat
        if j=a[x+1] then
        begin
          inc(T[x+1,i]); break;
        end else if x=0 then inc(T[0,i]);
        x:=p[x];
      until x=-1;
    end;
  {for i:=0 to m-1 do
  begin
    for j:=0 to m-1 do write(T[i,j],' ');
    writeln;
  end;} 
end;
//============================================================================
procedure Quick_power(x:longint);
var i,j,k:longint;
begin
  if x=1 then
  begin
    tt:=T; exit;
  end; Quick_power(x div 2);
  fillchar(tmp,sizeof(tmp),0);
  for i:=0 to m-1 do
    for j:=0 to m-1 do
      for k:=0 to m-1 do
        tmp[i,j]:=(tt[i,k]*tt[k,j]+tmp[i,j]) mod kk;
  tt:=tmp;
  fillchar(tmp,sizeof(tmp),0);
  if x mod 2=1 then
  begin
    for i:=0 to m-1 do
      for j:=0 to m-1 do
        for k:=0 to m-1 do
          tmp[i,j]:=(tt[i,k]*T[k,j]+tmp[i,j]) mod kk;
    tt:=tmp;
  end;
end;
//============================================================================
procedure DP;
var i,j,ans:longint;
begin
  s[0]:=9; s[1]:=1;
  for i:=0 to m-1 do
    for j:=0 to m-1 do
      f[i]:=(f[i]+s[j]*tt[i,j]) mod kk;
  ans:=0;
  for i:=0 to m-1 do ans:=(ans+f[i]) mod kk;
  writeln(ans);
end;
//============================================================================
begin
  init;
  KMP;
  Set_Matrix;     //建立矩阵。
  Quick_power(n-1);     //转移矩阵的快速幂。
  DP;
end.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值