[BJOI2017]魔法咒语
作者:互联网
Description
Chandra 是一个魔法天才。
从一岁时接受火之教会洗礼之后, Chandra 就显示出对火元素无与伦比的亲和力,轻而易举地学会种种晦涩难解的法术。这也多亏 Chandra 有着常人难以企及的语言天赋,让她能轻松流利地说出咒语中那些极其拗口的魔法词汇。
直到十四岁,开始学习威力强大的禁咒法术时, Chandra 才遇到了障碍。
根据火之魔法规则,禁咒的构成单位是 N 个基本词汇。施法时只要凝聚精神力,说出一段用这些词语组成的长度恰好等于 L 的语言,就能释放威力超乎想象的火法术。过去的魔法师们总结了几种表达起来最连贯的组合方式,方便施法者以最快语速完成法术。
但具有魔法和语言双重天才的 Chandra 不满足于这几种流传下来的禁咒,因为她可以毫无困难地说出普通人几乎不可能表达的禁咒语句。然而,在实际施法时, Chandra 发现有些自创禁咒念出后不但没有预期效果,反而会使自己的精神力迅速枯竭,十分难受。
这个问题令 Chandra 万分不解。她大量阅读典籍,到处走访魔法学者,并且不顾精神折磨一次又一次尝试新咒语,希望找出问题的答案。
很多年过去了,在一次远古遗迹探险中, Chandra 意外闯进了火之神艾利克斯的不知名神殿。根据岩土特征分析,神殿应该有上万年的历史,这是极其罕见的。 Chandra 小心翼翼地四处探索,沿着魔力流动来到一间密室。她看见密室中央悬浮着一本书籍。在魔法保护下书籍状况完好。精通上古语言的 Chandra 读过此书,终于解开了多年的困惑。
禁咒法术之所以威力强大,是因为咒语借用了火之神艾利克斯的神力。这本书里记载了艾利克斯生平忌讳的 M 个词语,比如情敌的名字、讨厌的植物等等。使用禁咒法术时,如果语言中含有任何忌讳词语,就会触怒神力而失效,施法者也一并遭受惩罚。
例如,若 ”banana” 是唯一的忌讳词语, “an”、 ”ban”、 ”analysis” 是基本词汇,禁咒长度须是 11, 则“bananalysis” 是无效法术, ”analysisban”、 ”anbanbanban”是两个有效法术。注意:一个基本词汇在禁咒法术中可以出现零次、 一次或多次;只要组成方式不同就认为是不同的禁咒法术,即使书写形式相同。
谜题破解, Chandra 心情大好。她决定计算一共有多少种有效的禁咒法术。
由于答案可能很大,你只需要输出答案模 1,000,000,007的结果。
Input
第一行,三个正整数 N, M, L。
接下来 N 行,每行一个只含小写英文字母的字符串,表示一个基本词汇。
接下来 M 行,每行一个只含小写英文字母的字符串,表示一个忌讳词语。
对于60%的数据1<=N,M<=50,L<=100
对于另40%数据基本词汇长度不超过2,L<=10^8
Output
仅一行,一个整数,表示答案(模 10^9+7)。
Sample Input
4 2 10
boom
oo
ooh
bang
ob
mo
Sample Output
14
BJOI的题感觉就是疯狂分类讨论(一题更比两题强)
首先考虑60%的数据,对禁忌串建立AC自动机,然后设\(f[i][j]\)表示长度为\(i\),匹配到AC自动机的第\(j\)个位置的方案数,转移的时候则需要枚举基本词汇,判断其从\(j\)开始匹配时是否会出现禁忌词汇,否则记匹配完后的位置为\(k\),由\(f[i][j]\)转移到\(f[i+len][k]\)即可
考虑len=1的部分,那么\(f[i][j]\)必然会转移到\(f[i+1][k]\),那么我们就可以用矩阵乘法加速转移,具体而言,如果\(f[i][j]\)可以转移到\(f[i+1][k]\),则转移矩阵\(Tr[j][k]\)加1即可
考虑len=2的部分,类比一下我们求Fibonacci数列时使用的矩阵,同样的,我们记录两个信息,表示\(f[0],f[1]\)(第二维我就不写了),那么转移矩阵就自己画图弄一弄吧
/*program from Wolfycz*/
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define inf 0x7f7f7f7f
using namespace std;
typedef long long ll;
typedef unsigned int ui;
typedef unsigned long long ull;
inline char gc(){
static char buf[1000000],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++;
}
inline int frd(){
int x=0,f=1;char ch=gc();
for (;ch<'0'||ch>'9';ch=gc()) if (ch=='-') f=-1;
for (;ch>='0'&&ch<='9';ch=gc()) x=(x<<1)+(x<<3)+ch-'0';
return x*f;
}
inline int read(){
int x=0,f=1;char ch=getchar();
for (;ch<'0'||ch>'9';ch=getchar()) if (ch=='-') f=-1;
for (;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+ch-'0';
return x*f;
}
inline void print(int x){
if (x<0) putchar('-'),x=-x;
if (x>9) print(x/10);
putchar(x%10+'0');
}
const int N=1e2,Mod=1e9+7;
int n,m,L;
char S[N+10][N+10];
struct S1{
int trie[N+10][26],fail[N+10],tot,root;
bool End[N+10];
void insert(char *s){
int len=strlen(s),p=root;
for (int i=0;i<len;i++){
if (!trie[p][s[i]-'a']) trie[p][s[i]-'a']=++tot;
p=trie[p][s[i]-'a'];
}
End[p]=1;
}
void make_fail(){
static int h[N+10];
int head=1,tail=0;
for (int i=0;i<26;i++) if (trie[root][i]) h[++tail]=trie[root][i];
for (;head<=tail;head++){
int Now=h[head];
End[Now]|=End[fail[Now]];
for (int i=0;i<26;i++){
if (trie[Now][i]){
int son=trie[Now][i];
fail[son]=trie[fail[Now]][i];
h[++tail]=son;
}else trie[Now][i]=trie[fail[Now]][i];
}
}
}
int check(char *s,int Fir){
int len=strlen(s),p=Fir;
for (int i=0;i<len;i++){
p=trie[p][s[i]-'a'];
if (End[p]) return -1;
}
return p;
}
}AC;//Aho-Corasick automaton
namespace DP{
int f[N+10][N+10];
void main(){
f[0][0]=1;
for (int i=0;i<L;i++){
for (int j=0;j<=AC.tot;j++){
if (!f[i][j]) continue;
for (int k=1;k<=n;k++){
int tmp=AC.check(S[k],j),len=strlen(S[k]);
if (!~tmp||i+len>L) continue;
f[i+len][tmp]=(f[i+len][tmp]+f[i][j])%Mod;
}
}
}
int Ans=0;
for (int i=0;i<=AC.tot;i++) Ans=(Ans+f[L][i])%Mod;
printf("%d\n",Ans);
}
};
namespace Matrix{
int T;
struct Matrix{
int v[(N<<1)+10][(N<<1)+10];
Matrix(){memset(v,0,sizeof(v));}
void init(){for (int i=0;i<T;i++) v[i][i]=1;}
}Trans;
Matrix operator *(const Matrix &x,const Matrix &y){
Matrix z;
for (int i=0;i<T;i++)
for (int j=0;j<T;j++)
for (int k=0;k<T;k++)
z.v[i][k]=(1ll*x.v[i][j]*y.v[j][k]+z.v[i][k])%Mod;
return z;
}
Matrix mlt(Matrix a,int b){
Matrix res; res.init();
for (;b;b>>=1,a=a*a) if (b&1) res=res*a;
return res;
}
void main(){
Matrix Ans; Ans.v[0][0]=1; T=(AC.tot<<1)+2;
for (int i=0;i<=AC.tot;i++) Trans.v[AC.tot+1+i][i]=1;
for (int i=0;i<=AC.tot;i++){
for (int j=1;j<=n;j++){
int tmp=AC.check(S[j],i),len=strlen(S[j]);
if (!~tmp) continue;
if (len==1){
Trans.v[i+AC.tot+1][tmp+AC.tot+1]++;
if (!i) Ans.v[0][tmp+AC.tot+1]++;
}else Trans.v[i][tmp+AC.tot+1]++;
}
}
Ans=Ans*mlt(Trans,L);
int res=0;
for (int i=0;i<=AC.tot;i++) res=(res+Ans.v[0][i])%Mod;
printf("%d\n",res);
}
};
int main(){
n=read(),m=read(),L=read();
for (int i=1;i<=n;i++) scanf("%s",S[i]);
for (int i=1;i<=m;i++){
static char s[N+10];
scanf("%s",s);
AC.insert(s);
}
AC.make_fail();
if (L<=1e2){
DP::main();
return 0;
}
Matrix::main();
return 0;
}
标签:10,ch,int,魔法,BJOI2017,法术,禁咒,Chandra,咒语 来源: https://www.cnblogs.com/Wolfycz/p/10486282.html