【BZOJ2553】[BeiJing2011]禁忌 AC自动机+期望DP+矩阵乘法

【BZOJ2553】[BeiJing2011]禁忌

Description

Magic Land上的人们总是提起那个传说:他们的祖先John在那个东方岛屿帮助Koishi与其姐姐Satori最终战平。而后,Koishi恢复了读心的能力……

如今,在John已经成为传说的时代,再次造访那座岛屿的人们却发现Koishi遇到了新麻烦。

这次她遇到了Flandre Scarlet——她拥有可以使用禁忌魔法而不会受到伤害的能力。

为了说明什么是禁忌魔法及其伤害,引入以下概念:

1.字母集A上的每个非空字符串对应了一个魔法。

其中A是包含了前alphabet个小写字母的集合。

2.有一个集合T,包含了N个字母集A上的字符串

T中的每一串称为一个禁忌串(Taboo string

3.一个魔法,或等价地,其对应的串s因为包含禁忌而对使用者造成的伤害按以下方式确定:

s分割成若干段,考虑其中是禁忌串的段的数目,不同的分割可能会有不同的数目,其最大值就是这个伤害。

由于拥有了读心的能力,Koishi总是随机地使用Flandre Scarlet的魔法,可以确定的是,她的魔法正好对应字母集A上所有长度为len的串

但是,Flandre Scarlet所使用的一些魔法是带有禁忌的,由于其自身特性,她可以使用禁忌魔法而不受到伤害,而Koishi就不同了。可怜的Koishi每一次使用对方的魔法都面临着受到禁忌伤害的威胁。

你现在需要计算的是如果Koishi使用对方的每一个魔法的概率是均等的,那么每一次随机使用魔法所受到的禁忌伤害的期望值是多少。

Input

第一行包含三个正整数Nlenalphabet

接下来N行,每行包含一个串Ti,表示禁忌串。

Output

一个非负实数,表示所受到禁忌伤害的期望值。

Sample Input

2 4 2
aa
abb

Sample Output

0.75
【样例1解释】
一共有2^4 = 16种不同的魔法。
需要注意的是“aabb”的禁忌伤害是1而不是2。

HINT

100%的数据中N ≤ 5len ≤1091 ≤ alphabet ≤ 26

在所有数据中,有不少于40%的数据中:N = 1

数据保证每个串Ti的长度不超过15,并且不是空串。

数据保证每个Ti均仅含有前alphabet个小写字母。

数据保证集合T中没有相同的元素,即对任意不同的ij,有TiTj

题解:做完GT考试那道题在做这道题感觉就有思路了

直接建出Trie图,然后根据Trie图构造转移矩阵,具体方法:

(std)对于AC自动机的节点i的j号儿子指针,若j不是危险节点(危险节点:一个禁忌字符串的结尾),那么直接令转移矩阵的[i,j]=1/alphabet;若j是危险节点,则直接令[i,root]=1/alphabet(因为要求禁忌串不能重叠),然后需要记录它对答案的贡献,那么新建一个节点0,再令[i,0]=1/alphabet,[0,0]=1就好了

目标矩阵[1,1]=1,然后目标矩阵*=转移矩阵^len,答案就是目标矩阵的[1,0]

(my)谁能告诉我我这方法错在哪啊啊啊!!!

对于节点i,如果它本身是危险节点,那么[i,0]=0,然后[i,j]=1/alphabet(j是根节点的儿子);若不是,那么[i,j]=1/alphabet(j是i的儿子)

帮我看看代码吧~

std:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <queue>
using namespace std;
typedef long double ld;
int n,len,m,tot;
char str[20];
queue<int> q;
struct node
{
	int ch[26],fail,cnt;
}p[100];
typedef struct matrix
{
	ld v[100][100];
}M;
M x,ans,emp;
void build()
{
	q.push(1);
	int i,u;
	while(!q.empty())
	{
		u=q.front(),q.pop();
		for(i=0;i<m;i++)
		{
			if(u==1)
			{
				if(!p[u].ch[i])	p[u].ch[i]=1;
				else	p[p[u].ch[i]].fail=1,q.push(p[u].ch[i]);
				continue;
			}
			if(!p[u].ch[i])
			{
				p[u].ch[i]=p[p[u].fail].ch[i];
				continue;
			}
			q.push(p[u].ch[i]);
			p[p[u].ch[i]].fail=p[p[u].fail].ch[i];
			p[p[u].ch[i]].cnt|=p[p[p[u].fail].ch[i]].cnt;
		}
	}
}
M mmul(M a,M b)
{
	M c=emp;
	int i,j,k;
	for(i=0;i<=tot;i++)
		for(j=0;j<=tot;j++)
			for(k=0;k<=tot;k++)
				c.v[i][j]+=a.v[i][k]*b.v[k][j];
	return c;
}
void pm(int y)
{
	while(y)
	{
		if(y&1)	ans=mmul(ans,x);
		x=mmul(x,x),y>>=1;
	}
}
int main()
{
	scanf("%d%d%d",&n,&len,&m);
	int i,j,a,b,u;
	tot=1;
	for(i=1;i<=n;i++)
	{
		scanf("%s",str),a=strlen(str);
		u=1;
		for(j=0;j<a;j++)
		{
			b=str[j]-‘a‘;
			if(!p[u].ch[b])	p[u].ch[b]=++tot;
			u=p[u].ch[b];
		}
		p[u].cnt=1;
	}
	build();
	x.v[0][0]=1.0,ans.v[1][1]=1.0;
	for(i=1;i<=tot;i++)
	{
		for(j=0;j<m;j++)
			if(p[p[i].ch[j]].cnt)	x.v[i][0]+=(ld)1/m,x.v[i][1]+=(ld)1/m;
			else	x.v[i][p[i].ch[j]]+=(ld)1/m;
	}
	pm(len);
	printf("%.7f",(double)ans.v[1][0]);
	return 0;
}

my:(WA)

#include <cstdio>
#include <cstring>
#include <iostream>
#include <queue>
using namespace std;
typedef long double ld;
int n,len,m,tot;
char str[20];
queue<int> q;
struct node
{
	int ch[26],fail,cnt;
}p[100];
typedef struct matrix
{
	ld v[100][100];
}M;
M x,ans,emp;
void build()
{
	q.push(1);
	int i,u;
	while(!q.empty())
	{
		u=q.front(),q.pop();
		for(i=0;i<m;i++)
		{
			if(u==1)
			{
				if(!p[u].ch[i])	p[u].ch[i]=1;
				else	p[p[u].ch[i]].fail=1,q.push(p[u].ch[i]);
				continue;
			}
			if(!p[u].ch[i])
			{
				p[u].ch[i]=p[p[u].fail].ch[i];
				continue;
			}
			q.push(p[u].ch[i]);
			p[p[u].ch[i]].fail=p[p[u].fail].ch[i];
			p[p[u].ch[i]].cnt|=p[p[p[u].fail].ch[i]].cnt;
		}
	}
}
M mmul(M a,M b)
{
	M c=emp;
	int i,j,k;
	for(i=0;i<=tot;i++)
		for(j=0;j<=tot;j++)
			for(k=0;k<=tot;k++)
				c.v[i][j]+=a.v[i][k]*b.v[k][j];
	return c;
}
void pm(int y)
{
	while(y)
	{
		if(y&1)	ans=mmul(ans,x);
		x=mmul(x,x),y>>=1;
	}
}
int main()
{
	scanf("%d%d%d",&n,&len,&m);
	int i,j,a,b,u;
	tot=1;
	for(i=1;i<=n;i++)
	{
		scanf("%s",str),a=strlen(str);
		u=1;
		for(j=0;j<a;j++)
		{
			b=str[j]-‘a‘;
			if(!p[u].ch[b])	p[u].ch[b]=++tot;
			u=p[u].ch[b];
		}
		p[u].cnt=1;
	}
	build();
	x.v[0][0]=1.0,ans.v[1][1]=1.0;
	for(i=1;i<=tot;i++)
	{
		if(p[i].cnt)
		{
			x.v[i][0]+=1.0;
			for(j=0;j<m;j++)	x.v[i][p[1].ch[j]]+=(ld)1/m;
		}
		else	for(j=0;j<m;j++)	x.v[i][p[i].ch[j]]+=(ld)1/m;
	}
	pm(len+1);
	printf("%.7f",(double)ans.v[1][0]);
	return 0;
}
时间: 2024-08-27 21:47:42

【BZOJ2553】[BeiJing2011]禁忌 AC自动机+期望DP+矩阵乘法的相关文章

【BZOJ 2553】[BeiJing2011]禁忌 AC自动机+期望概率dp

我一开始想的是倒着来,发现太屎,后来想到了一种神奇的方法——我们带着一个既有期望又有概率的矩阵,偶数(2*id)代表期望,奇数(2*id+1)代表概率,初始答案矩阵一列,1的位置为1(起点为0),工具矩阵上如果是直接转移那么就是由i到j概率期望都乘上1/alphabet,特别的,对于一个包含禁忌串的节点直接由其父节点指向0,而且在计算期望是多加上他的概率,最后统计答案时把答案矩阵上所有的期望加和即可,这个方法很完美的被卡精了....... #include <cstdio> #include

【BZOJ1444】[Jsoi2009]有趣的游戏 AC自动机+概率DP+矩阵乘法

[BZOJ1444][Jsoi2009]有趣的游戏 Description Input 注意 是0<=P Output Sample Input Sample Output HINT  30%的数据保证, n ≤ 2. 50%的数据保证, n ≤ 5. 100%的数据保证, n , l, m≤ 10. 题解:本题的做法真的很多啊,概率DP,期望DP,当然还有矩乘黑科技~ 就是先跑AC自动机,弄出转移矩阵,然后自乘50次就行了. #include <cstdio> #include <

BZOJ 2553 BeiJing2011 禁忌 AC自动机+矩阵乘法

题目大意:给定n个模式串,定义一个字符串的伤害为所有子串的划分中最多包含的模式串数量,求长度为len的字符串的伤害期望值 小五prpr,恋恋prpr,大小姐prpr 首先建立AC自动机 令f[i][j]表示长度为i的字符串在AC自动机上的第j个节点的伤害期望值 如果要走到某个节点是危险节点或者fail指针指向危险节点,就ans++,然后回到根节点 这样构造出来的矩阵做快速幂= = 这么做都会把- - 不会别骂我- - 但是跑完发现找不到答案- - 因此我们需要稍微改造一下- - 新建一个节点 如

【AC自动机】【矩阵乘法】poj2778 DNA Sequence

http://blog.csdn.net/morgan_xww/article/details/7834801 讲得很好~可以理解自动机的本质,就是一个用来状态转移的东西~对于确定的输入而言,可以从初始状态,按照转移边,转移到确定的终止状态. 而这种转移可以用矩乘加速. #include<cstdio> #include<cstring> #include<vector> #include<queue> #include<iostream> us

[AC自动机+概率dp] hdu 3689 Infinite monkey theorem

题意: 给n个字母,和m次数. 然后输入n个字母出现的概率 然后再给一个目标串str 然后问m次中敲出目标串的概率是多少. 思路: AC自动机+概率dp的简单题. 首先建立trie图,然后就是状态转移了 dp版本: dp三重循环变量次数,节点数,和字母数 代码: #include"cstdlib" #include"cstdio" #include"cstring" #include"cmath" #include"

POJ 3691 DNA repair 基于AC自动机的DP

dp[i][j] 表示长度为 i 的前缀到达第 j 个节点的最小更改数目. 很显然有dp[0][0] = 0; dp[ i ][ j ] = min(dp[ i ][ j ],dp[i-1][k] + (j == k ? 0 : 1)),当且仅当j,k满足下列条件时. j 不为某条模式串的末节点 且 j 到 root 的由失败指针组成的路径上无末节点. j 是k的儿子节点 或者 j 的父节点可由 k 沿着失败指针找到. #include <algorithm> #include <ios

HDU 4518 ac自动机+数位dp

吉哥系列故事--最终数 Time Limit: 500/200 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others) Total Submission(s): 304    Accepted Submission(s): 102 Problem Description 在2012年腾讯编程马拉松比赛中,吉哥解决了一道关于斐波那契的题目,这让他非常高兴,也更加燃起了它对数学特别是斐波那契数的热爱.现在,它又在思考一个关于斐波那契

zoj 3494 BCD Code(AC自动机+数位dp)

题目链接:zoj 3494 BCD Code 题目大意:给定n个2进制串,然后有一个区间l,r,问说l,r之间有多少个数转换成BCD二进制后不包含上面的2进制串. 解题思路:AC自动机+数位dp.先对禁止串建立AC自动机,所有的单词节点即为禁止通行的节点.接着进行数位dp, 用solve(r) - solve(l-1), 这里的l需要用到大数减法.dp[i][j]表示第i为移动到节点j的可行方案数,每次枚举下一位数字,因 为是BCD二进制,所以每位数要一次性移动4个字符,中途有经过禁止点都是不行

【bzoj3530】[Sdoi2014]数数 AC自动机+数位dp

题目描述 我们称一个正整数N是幸运数,当且仅当它的十进制表示中不包含数字串集合S中任意一个元素作为其子串.例如当S=(22,333,0233)时,233是幸运数,2333.20233.3223不是幸运数.给定N和S,计算不大于N的幸运数个数. 输入 输入的第一行包含整数N.接下来一行一个整数M,表示S中元素的数量.接下来M行,每行一个数字串,表示S中的一个元素. 输出 输出一行一个整数,表示答案模109+7的值. 样例输入 20 3 2 3 14 样例输出 14 题解 AC自动机+数位dp 同学