其他分享
首页 > 其他分享> > [BJOI2017]魔法咒语

[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