题意:

  将一个整数表示成4个bit的bcd码就成了一个01串,如果该串中出现了部分病毒串,则是危险的。给出n个病毒串(n<=100,长度<21),问区间[L,R]中有几个数字是不含病毒串的(结果需要取模)?(0<L<=R<=10200

思路:

  区间非常大,怎样暴力统计都是不科学的。首先确定状态,按传统,一维必定是位数,二维就是压缩的状态了,如果长度为20个bit的话,200*104万的数组是不行的。类似多模式串匹配问题,病毒串可以构建成AC自动机,那么每个点可以代表一个独立状态,而n<=100,所以最多20n个节点,是可以的。转移的话可以根据新考虑的数位是多少,然后在AC自动机上面走4步(BCD码是4bit)到达另一个状态(点),如果经过了病毒串的末尾节点,表示该数出现病毒串,就不能转移。这个可以在AC自动机创建完成后,预处理出来就行了。而对于每个询问[L,R],L仍然是需要减1的,大数减1比较简单。注意点是,AC自动机上的tag需要特殊处理,如果有病毒串"asdf"和串"sd",而碰到原串为"asdd",别忘了还有"sd"。这只需要在构建fail指针的时候处理一下。

 #include <bits/stdc++.h>
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <map>
#include <algorithm>
#include <vector>
#include <iostream>
#define pii pair<int,int>
#define INF 0x7f3f3f3f
#define LL long long
#define ULL unsigned long long
using namespace std;
const double PI = acos(-1.0);
const int N=;
const LL mod=; struct Trie{
static const int NN=; //节点数
static const int CC=; //孩子数
int next[NN][CC], fail[NN];
bool tag[NN];
int root, node_cnt;
int newnode(){
for(int i=; i<CC; i++) next[node_cnt][i]=-;
tag[node_cnt]=false; //刚创建时,默认非叶子
return node_cnt++;
}
void init(){
node_cnt=;
root=newnode(); //root是虚拟点
}
void insert(char s[]){
int len=strlen(s);
int now=root;
for(int i=; i<len; i++){
if(next[now][s[i]-'']==-)
next[now][s[i]-'']=newnode();
now=next[now][s[i]-''];
}
tag[now]=true; //尾节点:可能有多个相同模式串!
}
void buildAC(){
fail[root]=root; queue<int> que;
for(int i=; i<CC; i++){
if(next[root][i]==-)
next[root][i]=root;
else{
fail[next[root][i]]=root;
que.push(next[root][i]);
}
}
while(!que.empty()){
int now=que.front();que.pop();
if(tag[fail[now]]==true) tag[now]=true; //注意
for(int i=; i<CC; i++){
if( next[now][i]==-)
next[now][i]=next[fail[now]][i];
else{
fail[next[now][i]]=next[fail[now]][i];
que.push(next[now][i]);
}
}
}
}
}AC; LL f[N][N*];
int bcd[N*][], len;
char bit[N]; LL dfs(int i,int s,int sum,bool e) //s是节点编号
{
if(i==) return ;
if(!e&&~f[i][s]) return f[i][s]; LL ans=;
if(sum==) //处理前缀0
{
ans+=dfs(i-, s, , e&&bit[i]=='');
ans%=mod;
} int d= sum>? : ; //起
int u= e? bit[i]-'': ;//终
for(; d<=u; d++)
{
if(bcd[s][d]!=-)
{
ans+=dfs(i-, bcd[s][d], sum+d, e&&d==u);
ans%=mod;
}
}
if(!e&&sum) f[i][s]=ans; //没有前导零
return ans;
} LL cal()
{
reverse(bit+, bit+len+);
if(len==&&bit[len]=='') return ;
return dfs(len, , , true);
} int changeto(int s,int t)
{
if(AC.tag[s]) return -; //已经是病毒串
int now=s;
for(int i=; i>=; i--)
{
if( AC.tag[AC.next[now][(t>>i)&]]== ) return -; //病毒串
now=AC.next[now][(t>>i)&];
}
return now;
}
void pre_cal() //预处理转移
{
for(int i=; i<AC.node_cnt; i++)
for(int j=; j<; j++)
bcd[i][j]=changeto(i,j);
}
int main()
{
freopen("input.txt","r",stdin);
int t, n;cin>>t;
LL ans[];
while( t-- )
{
memset(bcd, -, sizeof(bcd));
memset(f, -, sizeof(f));
AC.init();
scanf("%d",&n);
for(int i=; i<n; i++)
{
scanf("%s",bit);
AC.insert(bit);
}
AC.buildAC(); //AC自动机
pre_cal(); //预处理转移 for(int j=; j<; j++)
{
scanf("%s", bit+);
len=strlen(bit+);
if(j==)
{
for(int i=len; i>; i--) //注意逆序
{
if( bit[i]>'' ){bit[i]--;break;}
else bit[i]='';
}
}
ans[j]=cal();
}
printf("%lld\n",(ans[]+mod-ans[])%mod);
}
return ;
}

AC代码

ZOJ 3494 BCD Code (数位DP,AC自动机)的更多相关文章

  1. ZOJ 3494 BCD Code(AC自动机 + 数位DP)题解

    题意:每位十进制数都能转化为4位二进制数,比如9是1001,127是 000100100111,现在问你,在L到R(R <= $10^{200}$)范围内,有多少数字的二进制表达式不包含模式串. ...

  2. 咕咕(数位dp+AC自动机)

    咕咕(数位dp+AC自动机) 若一个字符串的字符集合是0~m-1,那么称它为m进制字符串.给出n个m进制字符串\(s_i\),每个字符串的权值为\(v_i\).对于另一个m进制字符串\(S\),设\( ...

  3. ZOJ 3494 BCD Code(AC自动机+数位DP)

    BCD Code Time Limit: 5 Seconds      Memory Limit: 65536 KB Binary-coded decimal (BCD) is an encoding ...

  4. ZOJ 3494 BCD Code (AC自己主动机 + 数位DP)

    题目链接:BCD Code 解析:n个病毒串.问给定区间上有多少个转换成BCD码后不包括病毒串的数. 很奇妙的题目. . 经典的 AC自己主动机 + 数位DP 的题目. 首先使用AC自己主动机,得到b ...

  5. zoj 3494:BCD Code

    Description Binary-coded decimal (BCD) is an encoding for decimal numbers in which each digit is rep ...

  6. FZU 2113 BCD Code 数位dp

    数位dp,但是很奇怪的是我在虚拟oj上用GUC C++提交会wa,用Visual c++提交正确,但是加上注释后提交又莫名CE--好任性啊 0 ,0 题目思路:看代码吧 注释很详细 #include& ...

  7. 2019.02.15 codechef Favourite Numbers(二分+数位dp+ac自动机)

    传送门 题意: 给444个整数L,R,K,nL,R,K,nL,R,K,n,和nnn个数字串,L,R,K,数字串大小≤1e18,n≤65L,R,K,数字串大小\le1e18,n\le65L,R,K,数字 ...

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

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

  9. CF 434C Tachibana Kanade's Tofu[数位dp+AC自动机]

    Solution //本代码压掉后两维 #include<cstdio> #define max(a,b) (a<b?b:a) using namespace std; inline ...

随机推荐

  1. Centos7更新阿里yum源

    一.下载repo文件 wget http://mirrors.aliyun.com/repo/Centos-7.repo 二.备份并替换系统的repo文件 cp Centos-7.repo /etc/ ...

  2. C#连接Oracle数据库解决报错(需要安装Oracle客户端软件8.1.7)的问题

    1.通过nuget安装  ManagedDataAccess 2.引用 using Oracle.ManagedDataAccess.Client; 注:最低framework4.0 测试连接: // ...

  3. python dict操作

    d1 = {'one': 1, 'two': 2} d2 = {'one': 1, 'two': 2} d3 = {'one': 1, 'two': 2} print(dir(d1)) # 1.con ...

  4. 洛谷P1368 均分纸牌(加强版)

    P1368 均分纸牌(加强版) 题目描述 有 N 堆纸牌,编号分别为 1,2,…, N.每堆上有若干张,纸牌总数必为 N 的倍数.可以在任一堆上取1张纸牌,然后移动. 移牌规则为:在编号为 1 堆上取 ...

  5. Java基础--面向对象以及相关知识

    一. 面向对象特征与原则 1.三大特征:封装.继承.多态. (1)封装:将客观的事物封装成抽象的类,封装代码逻辑,并通过访问控制符来控制访问的方式,从而保护程序(使用反射时,可以获取对象的私有方法和成 ...

  6. 11.Python初窥门径(函数名,可迭代对象,迭代器)

    Python(函数名,可迭代对象,迭代器) 一.默认参数的坑 # 比较特殊,正常来说临时空间执行结束后应该删除,但在这里不是. def func(a,l=[]): l.append(a) return ...

  7. glassfish 自定义 jaas realm

    https://www.oschina.net/translate/custom-jaas-realm-for-glassfish-3

  8. C# 流

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...

  9. easyui---datalist相关知识

    datalist 笔记: class:class="easyui-datalist" //对应标准元素:ul 表格线:lines="true" 远程数据绑定: ...

  10. jdbc查询

    import java.util.ArrayList; import java.util.List; import org.springframework.jdbc.core.BeanProperty ...