• bzoj2553 禁忌


    题意:给定n个禁忌串。由字母表中的前alp的字母组成长度为len的字符串中,定义某个字符串的伤害为存在的一种分割方法使得其中禁忌串的数量的最大值(同一个禁忌串出现两次就算两次)。问期望伤害?

    n<=5,禁忌串长度<=15.len<=1e9.

    标程:

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<queue>
     4 using namespace std;
     5 typedef long double ld;
     6 const int N=80;
     7 int n,len,alp,ed,tag[N],son[N][26],sc,fail[N];
     8 ld p; queue<int> q; char s[N];
     9 struct node{ld g[N][N];}A;
    10 node operator * (node A,node B)
    11 {
    12     node c;
    13     for (int i=0;i<=ed;i++)//注意端点是从0到ed!
    14       for (int j=0;j<=ed;j++)
    15       {
    16           c.g[i][j]=0;  
    17         for (int k=0;k<=ed;k++)
    18           c.g[i][j]+=A.g[i][k]*B.g[k][j];
    19       }
    20     return c;
    21 }
    22 node ksm(node x,int y)
    23 {
    24    node res=x;y--;//技巧,避免繁冗的初始化 
    25    while (y){if (y&1) res=res*x; x=x*x; y>>=1;}
    26    return res;
    27 }
    28 void ins(char *s)
    29 {
    30     int now=0,sl=strlen(s);
    31     for (int i=0,c;i<sl;now=son[now][c],i++)
    32        if (!son[now][c=s[i]-'a']) son[now][c]=++sc;
    33     tag[now]=1;
    34 }
    35 void build()
    36 {
    37     for (int i=0;i<alp;i++) if (son[0][i]) fail[son[0][i]]=0,q.push(son[0][i]);
    38     while (!q.empty())
    39     {
    40         int now=q.front();q.pop();
    41         for (int i=0,y;i<alp;i++)
    42           if (y=son[now][i]) q.push(y),
    43             fail[y]=son[fail[now]][i],tag[y]|=tag[fail[y]];
    44           else son[now][i]=son[fail[now]][i];
    45     }
    46 }
    47 int main()
    48 {
    49     scanf("%d%d%d",&n,&len,&alp);
    50     for (int i=1;i<=n;i++) scanf("%s",s),ins(s);
    51     build();ed=sc+1;p=1.0/alp;
    52     for (int i=0;i<=sc;i++)
    53       for (int j=0;j<alp;j++)
    54         if (tag[son[i][j]]) A.g[i][0]+=p,A.g[i][ed]+=p;else A.g[i][son[i][j]]+=p;
    55     A.g[ed][ed]=1;
    56     printf("%.10lf
    ",(double)ksm(A,len).g[0][ed]);
    57     return 0;
    58 }

    易错点:1.注意AC自动机的节点数为sc,而不是n。矩阵转移的循环范围要注意。

    2.技巧:矩阵快速幂的时候可以先把res设为x,进行y-1次的幂操作,避免单位矩阵初始化。

    3.被卡精度。使用long double,输出时需要转double输出。

    题解:AC自动机+矩阵快速幂优化dp

    沿用梁大大的思路:15*5的串规模肯定是AC自动机。步长1e9就别想着枚举了,矩阵快速幂优化dp。

    这道题是分割方案,贪心来看,就是每次匹配到一个串,就返回起点重新来。这样就避免了串统计重叠。

    将所有禁忌串扔进AC自动机,并在禁忌节点打上标记(别往状压方向去想)。

    处理出A矩阵,A[i][j]表示从AC自动机上的i节点到j节点可以一步走到,权值设置成1/alp。如果下一个转移到禁忌串了,就跳到根,否则就往儿子走。为了方便统计答案,我们另设一个用来统计的终止节点T。跳到根的时候同时也跳往T。注意T到T的概率(权值)设为1。就是待在原地不再转移。A^len的[0][T]即是答案。

    看上去特别喵~对于一个串,它的伤害最大为k的分割方案中,每一个串都会在T终止一次,所以会被统计入k条权值为(1/alp)^len的路径,恰是这个串的伤害期望。

  • 相关阅读:
    nginx一键安装脚本
    nginx动静分离之后,设置默认主页
    日志备份
    cc高防主机部署
    原型和原型链
    Git&Github分支
    Git&Github基础
    传输层协议TCP&UDP
    本地库与远程库交互
    SVG
  • 原文地址:https://www.cnblogs.com/Scx117/p/8921684.html
Copyright © 2020-2023  润新知