10pts:

暴力枚举字符串,Hash判是否出现。(真会有人写么)

时间复杂度$O(10^n*n)$。


40pts:

学过OI的人都会写的dp

如果这道题的40pts($n\le 250000$)设成100pts的话那就是水的一批的提高组题目了。可惜这是省选题目

设$f(i,j)$表示以准考证号为基准递推,准考证号匹配到第$i$位,不吉利数字匹配第$j$位时(即准考证的后$j$位等于不吉利数字的前$j$位),不出现不吉利数字的字符串数量。

发现$m$只有20,$n*m$的dp可过。

那怎么转移?

既然要以考号为基准递推,就要考虑一位考号对下一位的影响。

而考号是可以随便写的,那我们就要考虑10种数字了。

对于一个新数字$new$,有以下几种情况:

1.$new$与不吉利数字的$j+1$位匹配,$dp(i+1,j+1)$的答案数+dp(i,j)

2.上述两者不匹配。

不匹配怎么办?这个不匹配的$new$一定没有贡献了?

当然不一定。

你有没有听说过一个叫$AC$自动机的垃圾。

啥,你没听说过?那放水点,你有没有听说过一个叫$KMP$的垃圾。

可能存在与$new$后缀与它相同的不吉利数字前缀,而这些前缀位置是要被更新答案的。

怎么讲呢,举个例子吧,不吉利数字是$12212112$。

然后你的准考证号枚举到第$9$位时,前面$8$位已经枚举成了$11112212$。可以发现已经匹配了不吉利数字的前$5$位,现在要匹配第$6$位。

如果第$9$位枚举$1$,那它就匹配了,$dp(9,6)+=dp(8,5)$。

如果第$9$位枚举$2$,那它就不匹配。但是会发现,存在 与当前已匹配的不吉利数字的后缀相同 的前缀,可以匹配上这个$2$!

上一个位置就是不吉利数字的第$2$位。

它的下一位,第$3$位$2$,刚好可以匹配枚举的第$9$位!

所以此时最多能匹配不吉利数字的前$3$位,有转移$dp(9,3)+=dp(8,5)$。

2019.8.23 update:这段好像解释错了,建议无视

我们只需要沿着不吉利数字的失配指针往前走,找到第一个下一位与$new$匹配的位置就可以了。

为什么不用考虑再往前的下一位可以匹配$new$的位置?

因为这是递推,从前往后每一种状态都会被考虑,所以在考虑匹配后面的位之前,前面的位已经匹配好更前面的情况了。

比如不吉利数字$1221221211$。你目前枚举的准考证号前$8$位是$12212212$,现在你要枚举第$9$位。

很明显当枚举$2$时,通过找不吉利数字中 后缀相同的前缀,可知$dp(8,8)$可以转移到$dp(9,6)$。

但是我们发现也可以转移到$dp(9,3)$诶!

事实上,在这之前$dp(8,5)$已经转移到$dp(9,3)$过了。而$dp(8,5)$表示什么?它表示准考证号枚举8位,后$5$位与不吉利数字的前$5$位匹配上。

$dp(8,8)$同理,表示准考证号枚举前$8$位,后$8$位与不吉利数字的前$5$位匹配上。

这样直观看起来没什么答案关联。但是仔细考虑以下,不吉利数字的第$8$位的失配指针指向第$5$位。

这说明什么?

不吉利数字的前$8$位中,前$5$位等于后$5$位!

还不够直观,能再明白点么?

匹配$5$位的情况包含匹配$8$位的情况!(因为你匹配了$8$位,根据上推论可知也算在匹配了$5$位的情况中)

这就是别人博客中此题题解经常提到的计数方案会包含的问题。蒟蒻之前也一直没看懂,想了一通才明白……

所以我们在转移时,把$dp(8,8)$的情况加给$dp(8,5)$,然后让$dp(8,5)$捎带加给$dp(9,3)$。

其实按照$AC$自动机的构造方式,如果一个点没有字符为$new$的儿子边的话,它会建出一条对应的虚拟儿子边和点,儿子点上存的是沿着失配指针往回走的上一个实际存在这条字符边所指向的儿子。

当然这题$m$很小,不吉利数字串自己匹配自己的复杂度很小,可以直接暴力跑失配指针找第一个。

转移就这样

$dp[i+1][j]=\sum_{0<=k<=m-1}dp[i][k] \times g[k][j]$

其中$g(i,j)$表示准考证号匹配不吉利数字的前$i$位时,准考证号增加一个字符,使不吉利数字沿失配指针(自己也可以)找到的最大的匹配位数$j$(算上新匹配的一位)的方案数。

$dp$套$dp$?

其实不用,不吉利数字已经知道了,$g$数组可以预处理出来($KMP$)。

注意一个事情,沿着失配指针走时,可能找不到与准考证号枚举位匹配的位置。此时新的最大匹配位数是$0$,仍然可以转移!

时间复杂度$O(n*m^2)$。


100pts:

然后我们惊奇地发现$n\le 10^9$,不让你循环推,直接就想到矩阵快速幂优化了。

观察一下转移方程,发现$dp[i+1][?]$总是由$dp[i][?]$推来,而且$i$还是$n$这个级别的。

又发现每次实际上都是乘一个固定的矩阵$g$,也就是说整个$dp$数组的某一位的值其实都是通过一些$g$数组乘过来的。

所以可以把$dp$数组直接当成$g$数组自己乘自己。

比如说,$g(4,2)=4$。

表示有4种转移情况可以让 与不吉利数字的前4位匹配的情况 在准考证号增加1位后 与不吉利数字的前2位匹配。

所以$dp(x+1,2)=dp(x,4)*g(4,2)=dp(x,4)*4$ | $x$是正整数。

而$dp(x,4)$又是哪来的?

它是通过$\sum_{i=1}^{m}dp(x-1,i)*g(i,4)$转移过来的。

所以一直往前推到x=0,发现$dp$值的$n$次转移都只跟转移矩阵$g$有关,是否与不吉利数字完全匹配等情况可以在弄$g$数组时就处理掉,即$g$数组只考虑不吉利数字被匹配$0$~$m-1$位的情况,不让它转移到$j$位都被匹配的情况。

所以就是对$g$矩阵做快速幂,求它的$n$次方。

时间复杂度$O(log(n)*m^3)$,有点常数。

最后说明:

1. 为什么答案是$\sum_{i=0}^{j-1}g(0,i)$:

考虑$g(i,j)$的意义。如果$g$矩阵自己对自己连续进行$k$次转移(不进行快速幂而循环推),$g(i,j)$就表示:在进行$k$次转移前 准考证号匹配不吉利数字的前$i$位时,准考证号增加$k$个字符后,使不吉利数字沿失配指针(自己也可以)找到的最大的匹配位数$j$(算上新匹配的$k$位)的方案数。这是矩阵转移的基本概念。

所以进行$n$次转移后,$g(i,j)$就表示一开始准考证号匹配不吉利数字的前$i$位时,准考证号增加$n$个字符后,使不吉利数字沿失配指针(自己也可以)找到的最大的匹配位数$j$(算上新匹配的$k$位)的方案数。

我们需要取全局情况的答案,而这是很显然的。开始时准考证号匹配不吉利数字的前$0$位(准考证号还一位都没枚举),所以$i$为$0$;而由于已经定义过$g$数组只考虑不吉利数字被匹配 $0$~$m-1$ 位的情况,所以$j$在这个区间取任意值,$g(0,j)$都是答案的一部分。把它们都算上就是答案。

2. (2018.11.26 update)初始矩阵是一维线性矩阵,而不是二维的!之前一直以为是二维的导致理解很麻烦!

下面是code,不过我的转移矩阵$b$(就是$g$)的$i,j$维是反过来写的,即 匹配后的匹配位数 指向 匹配前的匹配位数。

 #include<bits/stdc++.h>
#define M 21
#define ll long long
using namespace std;
inline int read(){
int x=; bool f=; char c=getchar();
for(;!isdigit(c);c=getchar()) if(c=='-') f=;
for(; isdigit(c);c=getchar()) x=(x<<)+(x<<)+(c^'');
if(f) return x;
return -x;
}
int n,m,mod,Fail[M];
char ch[M];
ll a[M][M],b[M][M];
void mul(ll a[M][M],ll b[M][M]){
ll tmp[M][M];
for(int i=;i<m;++i)
for(int j=;j<m;++j){
tmp[i][j]=;
for(int k=;k<m;++k)
(tmp[i][j]+=a[i][k]*b[k][j])%=mod;
}
for(int i=;i<m;++i)
for(int j=;j<m;++j)
a[i][j]=tmp[i][j];
}
int main(){
n=read(),m=read(),mod=read();
scanf("%s",ch+);
int i,j=;
for(i=;i<=m;++i){
while(j> && ch[j+]!=ch[i]) j=Fail[j];
if(ch[j+]==ch[i]) ++j;
Fail[i]=j;
}
int t;
for(i=;i<m;++i)
for(j=;j^;++j){
t=i;
while(t> && ch[t+]-''!=j) t=Fail[t];
if(ch[t+]-''==j) ++t;
if(t^m) b[t][i]=(b[t][i]+)%mod;
}
for(i=;i<m;++i) a[i][i]=;
while(n){
if(n&) mul(a,b);
mul(b,b);
n>>=;
}
ll sum=;
for(i=;i^m;++i) (sum+=a[i][])%=mod;
printf("%lld\n",sum);
return ;
}

【2018.10.10】[HNOI2008] GT考试(bzoj1009)的更多相关文章

  1. BZOJ1009 [HNOI2008]GT考试 矩阵

    去博客园看该题解 题目 [bzoj1009][HNOI2008]GT考试 Description 阿申准备报名参加GT考试,准考证号为N位数X1X2….Xn(0<=Xi<=9),他不希望准 ...

  2. 【BZOJ1009】[HNOI2008]GT考试 next数组+矩阵乘法

    [BZOJ1009][HNOI2008]GT考试 Description 阿申准备报名参加GT考试,准考证号为N位数X1X2....Xn(0<=Xi<=9),他不希望准考证号上出现不吉利的 ...

  3. [BZOJ1009] [HNOI2008] GT考试(KMP+dp+矩阵快速幂)

    [BZOJ1009] [HNOI2008] GT考试(KMP+dp+矩阵快速幂) 题面 阿申准备报名参加GT考试,准考证号为N位数X1X2-.Xn,他不希望准考证号上出现不吉利的数字.他的不吉利数学A ...

  4. 【bzoj1009】[HNOI2008]GT考试

    1009: [HNOI2008]GT考试 Time Limit: 1 Sec  Memory Limit: 162 MBSubmit: 3018  Solved: 1856[Submit][Statu ...

  5. bzoj1009 [HNOI2008] GT考试 矩阵乘法+dp+kmp

    1009: [HNOI2008]GT考试 Time Limit: 1 Sec  Memory Limit: 162 MBSubmit: 4542  Solved: 2815[Submit][Statu ...

  6. [Bzoj1009][HNOI2008]GT考试(KMP)(矩乘优化DP)

    1009: [HNOI2008]GT考试 Time Limit: 1 Sec  Memory Limit: 162 MBSubmit: 4309  Solved: 2640[Submit][Statu ...

  7. 申请Office 365一年免费的开发者账号攻略(2018年10月份版本)

    要进行Office 365开发,当然需要有完整的Office 365环境才可以.为了便于广大开发人员快速地启动这项工作,微软官方给所有开发人员提供了免费的一年开发者账号   那么如何申请Office ...

  8. IntelliJ IDEA 最新激活码(截止到2018年10月14日)

    IntelliJ IDEA 注册码: EB101IWSWD-eyJsaWNlbnNlSWQiOiJFQjEwMUlXU1dEIiwibGljZW5zZWVOYW1lIjoibGFuIHl1IiwiYX ...

  9. 新手C#SQL Server使用记录2018.08.10

    主键(PrimaryKey):主键就是每个数据行(记录)的唯一标识,不会有重复值的列(字段)才能当做主键.一个表可以没有主键,但是这样会很难处理表,因此一般情况表都要设置主键. 主键有两张选用策略,分 ...

随机推荐

  1. Java、Node.js、PHP还是.Net? 无论你选谁,我都能教你一招!

    七夕如期而至,不该来的终究还是来了.再傲娇的单身贵族恐怕也难免在今天会感觉一丝丝的空虚.还好你关注了我,因为接下来我准备了三大招教你一个人…..也可以优雅地过七夕. 招式一:移形幻影,无中生有 七夕当 ...

  2. 总结SQL Server窗口函数的简单使用

    总结SQL Server窗口函数的简单使用 前言:我一直十分喜欢使用SQL Server2005/2008的窗口函数,排名函数ROW_NUMBER()尤甚.今天晚上我在查看SQL Server开发的相 ...

  3. UVA10917 A walk trough the Forest (最短路,dp)

    求出家到其他点的最短路径,题目的条件变成了u->v不是回头路等价于d[u]>d[v]. 然后根据这个条件建DAG图,跑dp统计方案数,dp[u] = sum(dp[v]). #includ ...

  4. CAD命令标志

    CAD命令标志 主标识:(常用的)ACRX_CMD_MODAL 在别的命令执行的时候该命令不会在其中执行.ACRX_CMD_TRANSPARENT 命令可以再其它命令中执行,但在该标志下ads_sss ...

  5. OpenWrite方法打开现有文件并进行写入

    实现效果: 知识运用: File类的OpenWrite方法 //实现打开现有文件以进行写入 public static FileStream OpenWrite (string path) Encod ...

  6. css3中animation属性animation-timing-function知识点以及其属性值steps()

    在animation中最重要的其实就是时间函数(animation-timing-function)这个属性,他决定了你的动画将以什么样的速度执行,所以最关键的属性值也就是cubic-bezier(n ...

  7. shell脚本,按空格开始60秒的倒计时。

    [root@localhost wyb]# cat space.sh #!/bin/bash #按空格开始60秒的倒计时#-n表示接受字符的数量,1表示只接受一个字符  a() { - ` do ec ...

  8. Greenplum介绍-table

    GP中的table和其它关系型数据表是一样的,除了数据被分布在不同的segment以外. 建表时需定义以下几个方面:1. 指定列和数据类型2. 约束3. 分布策略4. 数据存储方式5. 大表分区策略 ...

  9. Linux基础学习-使用iSCSI服务部署网络存储

    使用iSCSI服务部署网络存储 iSCSI技术实现了物理硬盘设备与TCP/IP网络协议的相互结合,使得用户可以通过互联网方便地访问远程机房提供的共享存储资源.下面介绍如何在Linux上部署iSCSI服 ...

  10. modprode

    modprobe命令 1.modprobe 命令是根据depmod -a的输出/lib/modules/version/modules.dep来加载全部的所需要模块. 2.删除模块的命令是:modpr ...