BZOJ 1009 GT考试 (AC自动机 + 矩阵乘法加速dp)
题目链接:
https://www.lydsy.com/JudgeOnline/problem.php?id=1009
题意:
准考证号为\(n\)位数\(X_1X_2....X_n(0<=X_i<=9)\),你不希望准考证号上出现不吉利的数字。
不吉利数字\(A_1A_2...A_m(0<=A_i<=9)\)有\(m\)位,不出现是指\(X_1X_2...X_n\) 中没有恰好一段等于\(A_1A_2...A_m\)。\(A_1\) 和 \(X_1\) 都可以为\(0\)。
问你不出现不吉利数字的号码有多少种,输出模 \(mod\) 取余的结果。
\(n<=10^9,M<=20,mod<=1000\)。
题解:
对于这种间接的多模式匹配字符串+计数问题,我们可以用\(AC\)自动机 + \(dp\)。
\(dp[i][j]\)表示串长为 \(i\) , 匹配到了 \(AC\) 自动机的节点 \(j\),并且从来没有出现过完整的不吉利号码的方案数。
容易实现,\(dp\) 形式就是这样:
dp[0][0] = 1;
for(int i = 1; i <= n; i++) {
for(int j = 0; j <= sz; j++) {
for(int k = 0; k <= 9; k++) {
int v = ch[j][k];
if(End[v] == 0) {
dp[i][v] += dp[i - 1][j];
dp[i][v] %= mod;
}
}
}
}
int res = 0;
for(int i = 0; i <= sz; i++) {
res += dp[n][i];
res %= mod;
}
std::cout << "res = " << res << '\n';
但是\(n<=10^9\),时间复杂度会爆炸。
所以可以用矩阵快速幂来优化加速\(dp\)。
初始化矩阵\(tmp = [1,0,0]\),意思和 \(dp[0][0] = 1\) 的初始化一样。
将 \(x\) 节点与 \(x\) 的所有儿子节点 \(son\) 在转移矩阵 \(a\) 中都分别 \(a[x][son]+=1\)。
这样在矩阵乘法的转移时,可以把父亲的状态转移到儿子那里去。
最后答案即为 \(tmp * a ^ n\)。
建立失配指针时,需要注意:
如果一个数字的后缀是不吉利的(即fail指针指向不吉利数字的结尾节点), 那么这个数字一定也是不吉利的。
代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1000100;
int mod;
// AC自动机有三个部分,分别是建树,获取失配指针和查询。
// Aho_Corasick_Automaton :可以简单的理解为将KMP放在Trie树上
// https://www.lydsy.com/JudgeOnline/problem.php?id=1009
int End[maxn];
int ch[maxn][26];
int fail[maxn];
int sz;
struct matrix
{
int m[26][26];
void init() {
memset(m,0,sizeof(m));
}
};
matrix operator *(matrix a, matrix b)
{
matrix ans;
ans.init();
for(int i = 1; i <= sz + 1; i++) {
for(int j = 1; j <= sz + 1; j ++) {
for(int k = 1; k <= sz + 1; k++) {
ans.m[i][j] += a.m[i][k] * b.m[k][j];
ans.m[i][j] %= mod;
}
}
}
return ans;
}
matrix qpower(matrix a, int b)
{
matrix ans;
ans.init();
for(int i = 1; i <= sz + 1; i++) {
ans.m[i][i] = 1;
}
while(b > 0)
{
if(b & 1) ans = ans * a;
b >>= 1;
a = a * a;
}
return ans;
}
void insert(string s)
{
int now = 0;
for(int i = 0; i < (int)s.size();i++) {
int c = s[i] - '0';
if(!ch[now][c])ch[now][c] = ++sz;
now = ch[now][c];
}
End[now] = 1;
}
void getfail()
{
queue<int>que;
for(int i = 0; i <= 9; i++) {
if(ch[0][i]) {
que.push(ch[0][i]);
fail[ch[0][i]] = 0;
}
}
while(!que.empty())
{
int u = que.front();
que.pop();
for(int i = 0; i <= 9; i++) {
int v = ch[u][i];
if(v)
{
fail[v] = ch[fail[u]][i];
// 注意如果一个数字的后缀是不吉利的(即fail指针指向不吉利数字的结尾节点)
// 那么这个数字一定也是不吉利的
End[v] |= End[ch[fail[u]][i]];
que.push(v);
}
else ch[u][i] = ch[fail[u]][i];
}
}
}
string s;
int n,m;
int dp[maxn][26];
// dp[i][j]表示串长为 i , 匹配到了 AC 自动机的节点 j,并且从来没有出现过完整的不吉利号码的方案数。
int main(int argc, char const *argv[]) {
// freopen("in.txt","r",stdin);
std::cin >> n >> m >> mod;
std::cin >> s;
insert(s);
getfail();
// dp[0][0] = 1;
// for(int i = 1; i <= n; i++) {
// for(int j = 0; j <= sz; j++) {
// for(int k = 0; k <= 9; k++) {
// int v = ch[j][k];
// if(End[v] == 0) {
// dp[i][v] += dp[i - 1][j];
// dp[i][v] %= mod;
// }
// }
// }
// }
// int res = 0;
// for(int i = 0; i <= sz; i++) {
// res += dp[n][i];
// res %= mod;
// }
// std::cout << "res = " << res << '\n';
matrix a,tmp;
a.init();
tmp.init();
tmp.m[1][1] = 1;
for(int i = 0; i <= sz; i++) {
for(int j = 0; j <= 9; j++) {
int v = ch[i][j];
if(End[v] == 0) {
a.m[i + 1][v + 1] += 1;
}
}
}
a = tmp * qpower(a,n);
int ans = 0;
for(int i = 1; i <= sz + 1; i++) {
ans += a.m[1][i];
ans %= mod;
}
std::cout << ans << '\n';
return 0;
}
BZOJ 1009 GT考试 (AC自动机 + 矩阵乘法加速dp)的更多相关文章
- [BZOJ 1009] [HNOI2008] GT考试 【AC自动机 + 矩阵乘法优化DP】
题目链接:BZOJ - 1009 题目分析 题目要求求出不包含给定字符串的长度为 n 的字符串的数量. 既然这样,应该就是 KMP + DP ,用 f[i][j] 表示长度为 i ,匹配到模式串第 j ...
- bzoj 2553: [BeiJing2011]禁忌 AC自动机+矩阵乘法
题目大意: http://www.lydsy.com/JudgeOnline/problem.php?id=2553 题解: 利用AC自动机的dp求出所有的转移 然后将所有的转移储存到矩阵中,进行矩阵 ...
- P3193 [HNOI2008]GT考试(KMP+矩阵乘法加速dp)
P3193 [HNOI2008]GT考试 思路: 设\(dp(i,j)\)为\(N\)位数从高到低第\(i\)位时,不吉利数字在第\(j\)位时的情况总数,那么转移方程就为: \[dp(i,j)=dp ...
- 【bzoj1444】[Jsoi2009]有趣的游戏 AC自动机+矩阵乘法
题目描述 输入 注意 是0<=P 输出 样例输入 样例输出 题解 AC自动机+矩阵乘法 先将所有字符串放到AC自动机中,求出Trie图. 然后构建邻接矩阵:如果x不是某个字符串的末位置,则x连向 ...
- bzoj 1444 AC自动机 + 矩阵乘法 | 高斯消元
恶补了一下AC自动机,花了一天时间终于全部搞明白了. 思路:将每个人的串加入AC自动机,在AC自动机生成的状态图上建边,注意单词末尾的节点只能转移到自己概率为1, 然后将矩阵自乘几十次后误差就很小了, ...
- 【POJ2778】AC自动机+矩阵乘法
DNA Sequence Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 14758 Accepted: 5716 Descrip ...
- POJ 2778 DNA Sequence (AC自动机,矩阵乘法)
题意:给定n个不能出现的模式串,给定一个长度m,要求长度为m的合法串有多少种. 思路:用AC自动机,利用AC自动机上的节点做矩阵乘法. #include<iostream> #includ ...
- 洛谷 P4569 - [BJWC2011]禁忌(AC 自动机+矩阵乘法)
题面传送门 又好久没做过 AC 自动机的题了,做道练练手罢( 首先考虑对于某个固定的字符串怎样求出它的伤害,我们考虑贪心,每碰到出现一个模式串就将其划分为一段,最终该字符串的代价就是划分的次数.具体来 ...
- 【poj2778-DNA Sequence】AC自动机+矩阵乘法
题意: (只含AGCT)给定m个病毒串,让你构造一个长度为n的字符串(也只含有AGCT),问有多少种方案.n很大:1<=n<=2000000000 题解: 用病毒串建立AC自动机(num个 ...
随机推荐
- 关于Github Pages
迁移Github Pages 我稍微有一点强迫症,实在是忍受不了整洁的界面有一些推广的广告.正所谓博客平台不重要,重要的是要有干货,CSDN首页满屏的广告也就忍受了,但是自己的文章的页面有广告看着实在 ...
- Mac系统下安装ant
看了一些别人怎么在mac下安装ant,大体都是从官网下载bin文件,然后改动权限,建链接.配path. 须要这么麻烦吗?我认为不须要. 以下一个命令搞定: forlong401:build forlo ...
- android-继承BaseAdapter--自己定义适配器,getView运行多次的解决方法
定义的getView运行多次的ListView布局: <ListView android:id="@+id/lv_messages" android:layout_width ...
- legend---二、如何降低编程复杂度
legend---二.如何降低编程复杂度 一.总结 一句话总结:配置文件,数据库 个性的东西可以一起写入数据库,那么编程复杂度会大幅降低,页面灵活度也大幅降低(特有属性写进数据库) 比如不同难度的颜色 ...
- 8.不绑定(ngNonBindable)
转自:https://www.cnblogs.com/best/tag/Angular/ ngNonBindable指令告诉Angular编译或绑定当前DOM元素的内容.这对于要求Angular忽略那 ...
- 简单的quartz 可视化监听管理界面
spring-quartz. 导包.配置,不在此介绍. 简单的quartz管理界面,包括触发器的暂停.恢复.删除.修改(暂无),任务的运行.触发添加.创建,删除. 扩展内容:日志的管理,添加和创建触发 ...
- Android控件-TabHost(一)
什么是TabHost? TabHost组件的主要功能是可以进行应用程序分类管理,例如:在用户使用windows操作系统的时候,经常见到如图所示的图形界面. TabHost选项卡,说到这个组件, ...
- Git 操作笔记
¥先放个地址:这个视频超详细 https://www.bilibili.com/video/av10475153 分了2部分: 1是GitHub的功能介绍:2是pc端+doc命令的操作+发布个人网站 ...
- js 判断浏览器是否滚动到底部
//jquery 实现代码 $(document).height() == $(window).height() + $(window).scrollTop() 1 整个空间的高度 包含(滚动条距离顶 ...
- Facebook下载总结
Facebook是美国的一个社交网络服务网站,至今注册用户已超越20亿,月活用户更是惊人的突破3亿. 这样庞大的一个社交类网站,每日产生的社交数据当然也是非常可观,而这些社交数据,更接近口语,所以是比 ...