• 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
  • 相关阅读:
    [大山中学模拟赛] 2016.9.17
    [DP优化方法之斜率DP]
    Gengxin讲STL系列——String
    小班讲课之动态规划基础背包问题
    ubuntu安装体验
    小班出题之字符串基础检测
    G
    B
    小项目--反eclass
    树--天平问题
  • 原文地址:https://www.cnblogs.com/kafuuchino/p/10193188.html
Copyright © 2020-2023  润新知