BZOJ 1009: [HNOI2008]GT考试 AC自动机+矩阵快速幂

经典题目了....虽然只有一个不能出现的字符串,但还是写了ac自动机

1009: [HNOI2008]GT考试

Time Limit: 1 Sec  Memory Limit: 162 MB

Submit: 2051  Solved: 1257

[Submit][Status][Discuss]

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

HINT

Source

[Submit][Status][Discuss]

/* ***********************************************
Author        :CKboss
Created Time  :2015年05月12日 星期二 08时16分03秒
File Name     :BZOJ1009.cpp
************************************************ */

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <string>
#include <cmath>
#include <cstdlib>
#include <vector>
#include <queue>
#include <set>
#include <map>

using namespace std;
const int maxn=1200;

int idx(char c) { return c-'0'; }

int ch[maxn][11],fail[maxn],end[maxn];
int root,sz;
char str[maxn];

int newnode()
{
	memset(ch[sz],-1,sizeof(ch[sz]));
	end[sz++]=0;
	return sz-1;
}

void ac_init() { sz=0; root=newnode(); }

void ac_insert(char str[])
{
	int len=strlen(str);
	int now=root;
	for(int i=0;i<len;i++)
	{
		int id=idx(str[i]);
		if(ch[now][id]==-1) ch[now][id]=newnode();
		now=ch[now][id];
	}
	end[now]++;
}

void ac_build()
{
	queue<int> q;
	fail[root]=root;
	for(int i=0;i<10;i++)
	{
		if(ch[root][i]==-1)
			ch[root][i]=root;
		else
		{
			fail[ch[root][i]]=root;
			q.push(ch[root][i]);
		}
	}
	while(!q.empty())
	{
		int now=q.front(); q.pop();
		end[now]+=end[fail[now]];
		for(int i=0;i<10;i++)
		{
			if(ch[now][i]==-1)
				ch[now][i]=ch[fail[now]][i];
			else
			{
				fail[ch[now][i]]=ch[fail[now]][i];
				q.push(ch[now][i]);
			}
		}
	}
}

int n,m,mod;

typedef vector<int> vi;

struct MATRIX
{
	int _x,_y;
	int matrix[30][30];

	MATRIX(){};
	MATRIX(int n)
	{
		_x=_y=n;
		memset(matrix,0,sizeof(matrix));
	}

	void getOne(int n)
	{
		for(int i=0;i<n;i++)
			matrix[i][i]=1;
	}

	MATRIX operator* (const MATRIX& b) const
	{
		int n=b._x;
		MATRIX ret(n);
		for(int i=0;i<n;i++)
		{
			for(int j=0;j<n;j++)
			{
				int temp=0;
				for(int k=0;k<n;k++)
				{
					temp=(temp+matrix[i][k]*b.matrix[k][j])%mod;
				}
				ret.matrix[i][j]=temp%mod;
			}
		}
		return ret;
	}
};

MATRIX QuickPow(MATRIX M,int n)
{
	MATRIX ans;
	ans.getOne(M._x);
	while(n)
	{
		if(n&1) ans=ans*M;
		M=M*M;
		n/=2;
	}
	return ans;
}

int main()
{
	//freopen("in.txt","r",stdin);
	//freopen("out.txt","w",stdout);

	scanf("%d%d%d",&n,&m,&mod);
	scanf("%s",str);

	ac_init();
	ac_insert(str);
	ac_build();

	MATRIX mat(sz);

	for(int i=0;i<sz;i++)
	{
		if(end[i]) continue;
		for(int j=0;j<10;j++)
		{
			int p=ch[i][j];
			if(end[p]||end[fail[p]]) continue;
			mat.matrix[i][p]++;
		}
	}

	MATRIX ret=QuickPow(mat,n);

	int ans=0;

	for(int i=0;i<sz;i++)
	{
		ans=(ans+ret.matrix[0][i])%mod;
	}

	printf("%d\n",ans);

    return 0;
}
时间: 2024-10-15 14:32:15

BZOJ 1009: [HNOI2008]GT考试 AC自动机+矩阵快速幂的相关文章

BZOJ 1009 [HNOI2008]GT考试 AC自动机+矩阵乘法

题意:链接略 方法: AC自动机+矩阵乘法 解析: 和POJ 2778 一样的题. 大概的思路就是我们建AC自动机的时候需要注意如果某个点是一个串的结尾的话,那么下面的节点都要看成结尾节点. 然后按照AC自动机赋一下矩阵内部值就好了. 赋的矩阵代表从一个节点走一步走到另一个节点有多少方案. 然后经典模型,矩阵的n次方即可. 代码: #include <queue> #include <cstdio> #include <cstring> #include <ios

poj 2778 AC自动机 + 矩阵快速幂

// poj 2778 AC自动机 + 矩阵快速幂 // // 题目链接: // // http://poj.org/problem?id=2778 // // 解题思路: // // 建立AC自动机,确定状态之间的关系,构造出,走一步 // 能到达的状态矩阵,然后进行n次乘法,就可以得到状态间 // 走n步的方法数. // 精髓: // 1):这个ac自动机有一些特别,根节点是为空串,然而 // 每走一步的时候,如果没法走了,这时候,不一定是回到根 // 节点,因为有可能单个的字符时病毒,这样

POJ POJ 2778 DNA Sequence AC自动机 + 矩阵快速幂

首先建立Trie和失败指针,然后你会发现对于每个节点 i 匹配AGCT时只有以下几种情况: i 节点有关于当前字符的儿子节点 j 且安全,则i 到 j找到一条长度为 1的路. i 节点有关于当前字符的儿子节点 j 且 不安全,则i 到 j没有路. i 节点没有关于当前字符的儿子节点 但是能通过失败指针找到一个安全的节点j,那么 i 到 j 找到一条长度为1的路. 关于节点安全的定义: 当前节点不是末节点且当前节点由失败指针指回跟节点的路径上不存在不安全节点,那么这个节点就是安全节点. 然后问题就

hdu 2243 AC自动机 + 矩阵快速幂

// hdu 2243 AC自动机 + 矩阵快速幂 // // 题目大意: // // 给你一些短串,问在长度不超过k的任意串,包含至少一个这些短串的其中 // 一个.问这样的串有多少个. // // 解题思路: // // 首先, 包含和不包含是一种互斥关系,包含+不包含 = 全集u.全集的答案就是 // 26 ^ 1 + 26 ^ 2 + .... + 26 ^ k.不包含的比较好求.构建一个自动机,得到 // 一个转移矩阵A.表示状态i能到状态j的方法数.而这些状态中都是不包含所给的 //

HDU 2243 考研路茫茫――单词情结 (AC自动机 + 矩阵快速幂)

题目链接:考研路茫茫――单词情结 做本题前,个人建议先做一下POJ 2778 http://blog.csdn.net/u013446688/article/details/47378255 POJ2778 是求长度为n,不包含模式串的字符串个数. 而本题是求长度为n,包含模式串的字符串个数.直接用字符串总数减去不包含模式串的字符串个数即为所求. 同样是AC自动机 + 矩阵快速幂.但是还是有所不同的. 因为对2^64取模,所以定义数据类型为unsigned long long就可以了,这样就实现

HDU 2243 考研路茫茫――单词情结 ——(AC自动机+矩阵快速幂)

和前几天做的AC自动机类似. 思路简单但是代码200余行.. 假设solve_sub(i)表示长度为i的不含危险单词的总数. 最终答案为用总数(26^1+26^2+...+26^n)减去(solve_sub(1)+solve(2)+...+solve_sub(n)).前者构造f[i]=f[i-1]*26+26然后矩阵快速幂即可(当然也可以分治的方法).后者即构造出dp矩阵p,然后计算(p^1+p^2+...+p^n),对其分治即可. 代码如下: 1 #include <stdio.h> 2 #

poj2778DNA Sequence (AC自动机+矩阵快速幂)

转载请注明出处: http://www.cnblogs.com/fraud/          ——by fraud DNA Sequence Time Limit: 1000MS   Memory Limit: 65536K Description It's well known that DNA Sequence is a sequence only contains A, C, T and G, and it's very useful to analyze a segment of DN

poj 2778 DNA Sequence(AC自动机+矩阵快速幂)

题目链接:poj 2778 DNA Sequence 题目大意:给定一些含有疾病的DNA序列,现在给定DNA长度,问有多少种不同的DNA序列是健康的. 解题思路:对DNA片段建立AC自动机,因为最多10个串,每个串最长为10,所以最多可能有100个节点,在长度为n时 以每个节点终止的健康字符串个数形成一个状态集,通过AC自动机形成的边可以推导出n+1的状态集,走到单词节点是 非法的,所以同样的我们可以先走到单词节点,但是从单词节点不向后转移.这样可以构造一个矩阵,剩下的就是矩阵 快速幂.注意的一

poj2778 ac自动机+矩阵快速幂

给m个子串,求长度为n的不包含子串的母串数,最直接的应该是暴搜,肯定tle,考虑用ac自动机 将子串建成字典树,通过next表来构造矩阵,然后用矩阵快速幂求长度为n的数量 邻接矩阵https://wenku.baidu.com/view/d7b9787f1711cc7931b716b0.html 对于a(i,j)^k  是指从i到j经过k个点的所有情况数 注意对于End数组,如果某个节点如果fail指针End数组为1,那么End[该节点]也是1 string要开全局变量,不然不能运行= = #i