[HNOI2008]GT考试

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、付费专栏及课程。

余额充值