bzoj4861 / P3715 [BJOI2017]魔法咒语

P3715 [BJOI2017]魔法咒语

AC自动机+dp+矩阵乘法

常规思路是按基本串建立AC自动机

然鹅这题是按禁忌串建立AC自动机

对后缀是禁忌的点以及它的失配点做上标记$(a[i].ed)$,到时候不访问。

基本串转化为自动机上的:设$p[j][i]$表示第$j$个节点加上第$i$个串会到的节点编号

在建好AC自动机后可以直接处理。

现在分类讨论(对,两份代码)

1.$L<=100,60pts$

直接在AC自动机上跑dp

设$f[j][i]$表示到点$j$长度为$i$的方案数

枚举基本串$1~k$,显然$f[p[j][k]][i+size[k]]+=f[j][i]$

$sz$表示AC自动机的节点数,则$ans=\sum_{i=0}^{sz}f[i][L]*[a[i].ed==0]$

2.基本词汇长度不超过$2,40pts$

开个$maxn*2$的矩阵,矩阵乘法瞎搞。

$f[i][L]=\sum_{j}f[j][l-1],j->i$

对于长度为2的情况,就开2倍的数组,用$i*2+1$暂时保存。

注意下标从0开始

(为啥我的常数这么大呢......)

 1 #include<iostream>
 2 #include<cstring>
 3 #include<cstdio>
 4 #include<queue>
 5 using namespace std;
 6 #define N 105
 7 const int mod=1e9+7;
 8 int ans,sz,n,m,L,siz[N],f[N][N],p[N][N];
 9 char q[N],s[N][N];
10 queue <int> h;
11 
12 struct node{int nxt[26],f,ed;}a[5002];
13 void add(){
14     scanf("%s",q);
15     int len=strlen(q),u=0;
16     for(int i=0;i<len;++i){
17         if(!a[u].nxt[q[i]-'a'])
18             a[u].nxt[q[i]-'a']=++sz;
19         u=a[u].nxt[q[i]-'a'];
20     }a[u].ed=1;
21 }
22 void acbuild(){
23     for(int i=0;i<26;++i) if(a[0].nxt[i]) h.push(a[0].nxt[i]);
24     while(!h.empty()){
25         int x=h.front();h.pop();
26         for(int i=0;i<26;++i){
27             int &to=a[x].nxt[i];
28             if(to){
29                 a[to].f=a[a[x].f].nxt[i];
30                 a[to].ed|=a[a[to].f].ed;
31                 h.push(to);
32             }else to=a[a[x].f].nxt[i];
33         }
34     }
35 }
36 
37 struct mat{
38     int A[205][205];
39     mat(){memset(A,0,sizeof(A));}
40     mat operator * (mat &tmp) const{
41         mat c;int w=sz<<1|1;
42         for(int i=0;i<=w;++i)
43             for(int j=0;j<=w;++j)
44                 for(int k=0;k<=w;++k)
45                     c.A[i][j]=(c.A[i][j]+1ll*A[i][k]*tmp.A[k][j]%mod)%mod;
46         return c;
47     }
48 };
49 mat Pow(mat x,int y){
50     mat rs;
51     for(int i=0;i<=sz;++i)
52         rs.A[i<<1][i<<1]=1;
53     for(;y;y>>=1,x=x*x)
54         if(y&1) rs=rs*x;
55     return rs;
56 }
57 
58 void task1(){
59     f[0][0]=1;
60     for(int i=0;i<L;++i)
61         for(int j=0;j<=sz;++j)
62             if(f[j][i])
63                 for(int k=1;k<=n;++k)
64                     if(p[j][k]!=-1&&i+siz[k]<=L)
65                             f[p[j][k]][i+siz[k]]=(f[p[j][k]][i+siz[k]]+f[j][i])%mod;
66     for(int i=0;i<=sz;++i) if(!a[i].ed) ans=(ans+f[i][L])%mod;
67 }
68 void task2(){
69     mat res;
70     for(int i=0;i<=sz;++i){
71         for(int j=1;j<=n;++j)
72             if(p[i][j]!=-1){
73                 if(siz[j]==1) ++res.A[p[i][j]<<1][i<<1];
74                 else ++res.A[p[i][j]<<1][i<<1|1];
75             }
76         res.A[i<<1|1][i<<1]=1;
77     }res=Pow(res,L);
78     for(int i=0;i<=sz;++i) if(!a[i].ed) ans=(ans+res.A[i<<1][0])%mod;
79 }
80 int main(){
81     memset(p,-1,sizeof(p));
82     scanf("%d%d%d",&n,&m,&L);
83     for(int i=1;i<=n;++i) scanf("%s",s[i]),siz[i]=strlen(s[i]);
84     for(int i=1;i<=m;++i) add();
85     acbuild();
86     for(int i=1;i<=n;++i)
87         for(int j=0,u=0;j<=sz;u=++j){
88             for(int k=0;k<siz[i]&&!a[u].ed;++k)
89                 u=a[u].nxt[s[i][k]-'a'];
90             if(!a[u].ed) p[j][i]=u;
91         }
92     if(L<=100) task1();
93     else task2();
94     printf("%d",ans);
95     return 0;
96 }
View Code

 

posted @ 2018-12-28 22:22  kafuuchino  阅读(261)  评论(0编辑  收藏  举报