[SDOI2014]数数

这题的前置知识是AC自动机和dp,前置题目是 [JSOI2007]文本生成器,前置题目我写的题解 题解-[JSOI2007]文本生成器。我的讲解假设你做过上面那道题。

这题比上面那题多个条件,我因此多调了 \(3\) 个小时。多的条件:答案要不大于整数 \(n\)。所以AC自动机部分同上,改变dp部分。

解:\(dp[i][j][k]\) 表示文本串(幸运数)长度为 \(i\),结尾是AC自动机上的节点 \(j\),\(k\) 表示这个文本串下一个字符是否受 \(n\) 某个数位大小的限制(如果受限制,\(k=1\);否则,\(k=0\))。\(mk[i]\) 表示 \(i\) 这个AC自动机上节点是否为某个不幸运的数结尾。

仔细读题会发现:模式串中含有 \(0\) 前置,而文本串不能以 \(0\) 开头。所以有(所有数组下标从 \(1\) 开始,\(1\le n[1]\le 9\),因为 \(ch[1][i]\) 会有重复所以用 \(++\) 而非 \(=1\)):

\[dp[1][ch[1][i]][0]++(1\le i<n[1],mk[ch[1][i]]!=1)
\]

然后如果上式 \(i\) 取 \(n[1]\),那么这个字符串的下一位就会受到 \(n[2]\) 大小的限制,所以有:

\[dp[1][ch[1][n[1]]][1]++(mk[ch[1][n[1]]]!=1)
\]

综上,有代码:

for(int i=1;i<=w[1]-'0';i++)
if(!mk[ch[1][i]])//不能选到不幸运的子串
(f[1][ch[1][i]][i==w[1]-'0']+=1)%=mod; //Orz

为了避免算上首位为 \(0\) 的文本串,上面的代码没有 \(dp[1][ch[1][0]][0]++\)。为了计算那些位数小于 \(n\) 的文本串,则有:

\[dp[i][ch[1][j]][0]++(2\le i\le \texttt{length of }n,1\le j\le 9,mk[ch[1][j]]!=1)
\]

为了防止 \(\texttt{MLE}\),dp用滚动数组,所以有代码:

for(int i=2;i<=m;i++){
memset(f[i&1],0,sizeof f[i&1]);//滚动数组必须清空
for(int j=1;j<=9;j++)
if(!mk[ch[1][j]])
(f[i&1][ch[1][j]][0]+=1)%=mod;//Orz

初始化完了,重点就来了——递推公式。如果某个文本串合法,那么在它后面加一个字符,如果这个文本串还是 \(\le n\) ,并且不包含不幸运的子串,那么它就是合法的。

转化为dp递推式(\(cnt\) 表示AC自动机节点个数):

\[dp[i][ch[j][k]][0]+=dp[i-1][j][0]
\]
\[(1\le i\le\texttt{length of }n,1\le j\le cnt,mk[ch[j][k]]!=1,\color{red}0\color{black}\le k\le 9)
\]

这里是递推,所以这就相当于在求一个数中间的一个数位,所以可以取 \(0\)

\[dp[i][ch[j][k]][0]+=dp[i-1][j][1]
\]
\[(1\le i\le\texttt{length of }n,1\le j\le cnt,mk[ch[j][k]]!=1,\color{red}0\le k<n[i]\color{black})
\]

除非取的文本串对 \(n\) 位位紧逼,要不然下一位就不受 \(n\) 数位大小的限制。

\[dp[i][ch[j][n[i]]][1]+=dp[i-1][j][1]
\]
\[(1\le i\le\texttt{length of }n,1\le j\le cnt,mk[ch[j][n[i]]]!=1)
\]

取的文本串对 \(n\) 位位紧逼。

代码:

for(int j=1;j<=cnt;j++){
if(mk[j]) continue;
if(f[(i-1)&1][j][0])
for(int c=0;c<=9;c++)
if(!mk[ch[j][c]])
(f[i&1][ch[j][c]][0]+=f[(i-1)&1][j][0])%=mod;
if(f[(i-1)&1][j][1])
for(int c=0;c<=w[i]-'0';c++)
if(!mk[ch[j][c]])
(f[i&1][ch[j][c]][c==w[i]-'0']+=f[(i-1)&1][j][1])%=mod;
}

最后答案为 \(ans\),就有:

\[ans=\sum\limits ^{cnt}_{i=1}dp[\texttt{length of }n][i][0]+dp[\texttt{length of }n][i][1]
\]

如果你懂了,蒟蒻就放dp代码了:

void dp(){
for(int i=1;i<=w[1]-'0';i++)
if(!mk[ch[1][i]])
(f[1][ch[1][i]][i==w[1]-'0']+=1)%=mod; //Orz
for(int i=2;i<=m;i++){
memset(f[i&1],0,sizeof f[i&1]);
for(int j=1;j<=9;j++)
if(!mk[ch[1][j]])
(f[i&1][ch[1][j]][0]+=1)%=mod;//Orz
for(int j=1;j<=cnt;j++){
if(mk[j]) continue;
if(f[(i-1)&1][j][0])
for(int c=0;c<=9;c++)
if(!mk[ch[j][c]])
(f[i&1][ch[j][c]][0]+=f[(i-1)&1][j][0])%=mod;
if(f[(i-1)&1][j][1])
for(int c=0;c<=w[i]-'0';c++)
if(!mk[ch[j][c]])
(f[i&1][ch[j][c]][c==w[i]-'0']+=f[(i-1)&1][j][1])%=mod;
}
}
for(int i=1;i<=cnt;i++)
if(!mk[i]) (((ans+=f[m&1][i][0])%=mod)+=f[m&1][i][1])%=mod;
}

整体代码(dp+AC自动机):

#include <bits/stdc++.h>
using namespace std;
const int M=1210;
const int L=1510;
const int mod=1e9+7;
class Trie{
public:
int ch[L][10],cnt;
bool mk[L];
Trie(){cnt=1;}
void insert(char*s){
int n_junior=strlen(s+1),p=1;
for(int i=1;i<=n_junior;i++){
int c=s[i]-'0';
if(!ch[p][c]) ch[p][c]=++cnt;
p=ch[p][c];
}
mk[p]=1;
}
};
int n,m,f[2][L][2],ans;
char w[M],s[L];
class Acam:public Trie{
public:
int fa[L];
void build(){
for(int i=0;i<=9;i++) ch[0][i]=1;
queue<int> q;
while(q.size()) q.pop(); //我因为没清零WA了5次
q.push(1);
while(q.size()){
int x=q.front();q.pop();
mk[x]|=mk[fa[x]];
for(int c=0;c<=9;c++)
if(ch[x][c]){
fa[ch[x][c]]=ch[fa[x]][c];
q.push(ch[x][c]);
} else ch[x][c]=ch[fa[x]][c];
}
}
void dp(){
for(int i=1;i<=w[1]-'0';i++)
if(!mk[ch[1][i]])
(f[1][ch[1][i]][i==w[1]-'0']+=1)%=mod;
for(int i=2;i<=m;i++){
memset(f[i&1],0,sizeof f[i&1]);
for(int j=1;j<=9;j++)
if(!mk[ch[1][j]])
(f[i&1][ch[1][j]][0]+=1)%=mod;
for(int j=1;j<=cnt;j++){
if(mk[j]) continue;
if(f[(i-1)&1][j][0])
for(int c=0;c<=9;c++)
if(!mk[ch[j][c]])
(f[i&1][ch[j][c]][0]+=f[(i-1)&1][j][0])%=mod;
if(f[(i-1)&1][j][1])
for(int c=0;c<=w[i]-'0';c++)
if(!mk[ch[j][c]])
(f[i&1][ch[j][c]][c==w[i]-'0']+=f[(i-1)&1][j][1])%=mod;
}
}
for(int i=1;i<=cnt;i++)
if(!mk[i]) (((ans+=f[m&1][i][0])%=mod)+=f[m&1][i][1])%=mod;
}
}t;
int main(){
scanf("%s\n%d",w+1,&n),m=strlen(w+1);
for(int i=1;i<=n;i++)
scanf("%s",s+1),t.insert(s);
t.build(); t.dp();
printf("%d\n",ans);
return 0;
}

祝大家学习愉快!

题解-[SDOI2014]数数的更多相关文章

  1. BZOJ3530: [Sdoi2014]数数

    3530: [Sdoi2014]数数 Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 322  Solved: 188[Submit][Status] ...

  2. 【BZOJ】【3530】【SDOI2014】数数

    AC自动机/数位DP orz zyf 好题啊= =同时加深了我对AC自动机(这个应该可以叫Trie图了吧……出边补全!)和数位DP的理解……不过不能自己写出来还真是弱…… /************* ...

  3. 【HDU3530】 [Sdoi2014]数数 (AC自动机+数位DP)

    3530: [Sdoi2014]数数 Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 682  Solved: 364 Description 我们称一 ...

  4. BZOJ 3530: [Sdoi2014]数数 [AC自动机 数位DP]

    3530: [Sdoi2014]数数 题意:\(\le N\)的不含模式串的数字有多少个,\(n=|N| \le 1200\) 考虑数位DP 对于长度\(\le n\)的,普通套路DP\(g[i][j ...

  5. 「SDOI2014」数数 解题报告

    「SDOI2014」数数 题目描述 我们称一个正整数 \(N\) 是幸运数,当且仅当它的十进制表示中不包含数字串集合 \(S\) 中任意一个元素作为其子串. 例如当 \(S=(\)22, 333, 0 ...

  6. 3530: [Sdoi2014]数数

    3530: [Sdoi2014]数数 链接 分析: 对给定的串建立AC自动机,然后数位dp.数位dp的过程中,记录当前在AC自动机的哪个点上,保证不能走到出现了给定串的点. 代码: #include& ...

  7. [SDOI2014]数数 --- AC自动机 + 数位DP

    [SDOI2014]数数 题目描述: 我们称一个正整数N是幸运数,当且仅当它的十进制表示中不包含数字串集合S中任意一个元素作为其子串. 例如当S=(22,333,0233)时,233是幸运数,2333 ...

  8. bzoj [Sdoi2014]数数 AC自动机上dp

    [Sdoi2014]数数 Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 1264  Solved: 636[Submit][Status][Discu ...

  9. [Sdoi2014]数数[数位dp+AC自动机]

    3530: [Sdoi2014]数数 Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 834  Solved: 434[Submit][Status][ ...

随机推荐

  1. 三、分布式编程总结------linux多线程服务端编程

  2. sklearn.tree.DecisionTreeClassifier 详细说明

    sklearn.tree.DecisionTreeClassifier()函数用于构建决策树,默认使用CART算法,现对该函数参数进行说明,参考的是scikit-learn 0.20.3版本.     ...

  3. linux开机启动及运行级别、root密码丢失、单用户模式只读的处理方法

    linux系统启动大致步骤如下:  加电自检-->根据BIOS中的设置从指定的设备启动-->找到设备MBR中的bootloader引导启动系统-->启动kernel-->启动i ...

  4. 慢话crush-各种crush组合

    前言 ceph已经是一个比较成熟的开源的分布式存储了,从功能角度上来说,目前的功能基本能够覆盖大部分场景,而社区的工作基本上是在加入企业级的功能和易用性还有性能等方面在发力在,不管你是新手还是老手,都 ...

  5. Java-Annotation的一种用法(消除代码中冗余的if/else或switch语句)

    Java-Annotation的一种用法(消除代码中冗余的if/else或switch语句) 1.冗余的if/else或switch ​ 有没有朋友写过以下的代码结构,大量的if/esle判断,来选择 ...

  6. Vue知识点回顾(一)

    一.什么是vue? Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架.与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用.Vue 的核心库只关注视图层,不 ...

  7. 关于AOP思想,建议你看看这份五年开发总结的笔记,写的太详细了

    前言 OOP(Object Oriented Programing)面向对象编程 以对象为基本单位进行程序开发,通过对象间的彼此协同,相互协调,完成程序的构建 POP(Producer Oriente ...

  8. Java基础教程——缓冲流

    缓冲流 "缓冲流"也叫"包装流",是对基本输入输出流的增强: 字节缓冲流: BufferedInputStream , BufferedOutputStream ...

  9. 解决YUM下Loaded plugins: fastestmirror Determining fastest mirrors 的错误问题

    最近想再购买一台虚拟服务器做项目测试,之前在西部数码购买的已经过期了,在同事的推荐下去搬瓦工购买了一台服务器,听他介绍在这里购买服务器很便宜($19.99/年)而且还是国外的,看着相比之前的确实挺便宜 ...

  10. 锐安信sslTrus与GeoTrust SSL证书的对比

    当您想要为您的网站购买一款SSL证书时,是不是既想要价格便宜,又想要安全性能服务好的证书,最后又在品牌上犹豫不决?如何选择SSL证书?哪款SSL证书是最适合您的?本文将详细对比分析锐成信息平台上畅销品 ...