zoukankan      html  css  js  c++  java
  • [BZOJ2553]禁忌

    2553: [BeiJing2011]禁忌

    Time Limit: 20 Sec  Memory Limit: 128 MBSec  Special Judge

    Description

           Magic Land上的人们总是提起那个传说:他们的祖先John在那个东方岛屿帮助Koishi与其姐姐Satori最终战平。而后,Koishi恢复了读心的能力……

    如今,在John已经成为传说的时代,再次造访那座岛屿的人们却发现Koishi遇到了新麻烦。

           这次她遇到了Flandre Scarlet——她拥有可以使用禁忌魔法而不会受到伤害的能力。

           为了说明什么是禁忌魔法及其伤害,引入以下概念:

    1.字母集A上的每个非空字符串对应了一个魔法。

    其中A是包含了前alphabet个小写字母的集合。

    2.有一个集合T,包含了N个字母集A上的字符串

    T中的每一串称为一个禁忌串(Taboo string

    3.一个魔法,或等价地,其对应的串s因为包含禁忌而对使用者造成的伤害按以下方式确定:

               把s分割成若干段,考虑其中是禁忌串的段的数目,不同的分割可能会有不同的数目,其最大值就是这个伤害。

    由于拥有了读心的能力,Koishi总是随机地使用Flandre Scarlet的魔法,可以确定的是,她的魔法正好对应字母集A上所有长度为len的串

    但是,Flandre Scarlet所使用的一些魔法是带有禁忌的,由于其自身特性,她可以使用禁忌魔法而不受到伤害,而Koishi就不同了。可怜的Koishi每一次使用对方的魔法都面临着受到禁忌伤害的威胁。

    你现在需要计算的是如果Koishi使用对方的每一个魔法的概率是均等的,那么每一次随机使用魔法所受到的禁忌伤害的期望值是多少。

    Input

    第一行包含三个正整数N、len、alphabet。

    接下来N行,每行包含一个串Ti,表示禁忌串。

    Output

    一个非负实数,表示所受到禁忌伤害的期望值。

    Sample Input

    2 4 2
    aa
    abb

    Sample Output

    0.75
     
    题解:
    虽然已经打了一些概率,但是……字符串上的dp还真的是让人束手无策啊……
    在zyf神犇的提示之下总算是做出来了……
    首先,我们当然要给所有禁忌串建立trie树,然后我们来分析一下:
    如果有2个串A和B,满足A是B的前缀,那么B不可能比A更优,因为A和B中间可能还有其他的串
    因此,在插入的时候如果我们碰到一个单词节点,就直接跳出即可
    接下来,我们来构建ac自动机的fail指针,而且必须是trie图
    那么考虑对ac自动机上的节点,对于他的子节点:
    1、子结点没被标记(即不是禁忌串的结尾),有1/alphabet的概率转移到这个子结点,即f[i+1][k]+=f[i][j]/alphabet;
    2、子结点被标记,有1/alphabet的概率转移这个被标记的子节点,接着直接到根,并且累加答案+1。
    这也说明了为什么是trie图:不用trie图可能会漏状态
    由于统计答案很难,所以,我们设节点总数为cnt,设root编号为0,并且再开一个虚拟节点cnt+1,在经过这个点的时候累加答案
    也就是说标记点->cnt+1->root
    我们不难发现,上面这个式子,即f[i+1][k]+=f[i][j]/alphabet,是一个线性的递推式,因此可以用矩阵乘法优化,设矩阵为A[i][j]
    当矩阵乘法在图上应用时,A[i][j]的m次方表示从i点走m步到达j点时的某个值(边权,步数……之类的)
    那么在本题中,我们的答案就是A[0][cnt+1]
    设k=1/alphabet
    在我们初始化矩阵时候,如果有标记,那么走到根的概率加上k,伤害累加值加上k(概率是k,伤害是1)
    如果没有标记,直接加上概率k/1
    代码见下:
      1 #include<cstdio>
      2 #include<cstring>
      3 using namespace std;
      4 int n,len,size,cnt,hd=1,tl;
      5 char s[10][15];
      6 struct node
      7 {
      8     node *ch[26],*f;
      9     int id,mark;
     10     node(int v){memset(ch,0,sizeof(ch));id=v,mark=0;}
     11 }*root,*man[10010],*q[10100];
     12 struct Marx
     13 {
     14     long double m[160][160];
     15     void clear()
     16     {
     17         for(int i=0;i<160;i++)
     18             for(int j=0;j<160;j++)
     19                 m[i][j]=0;
     20     }
     21 }A,B,tmp;
     22 inline void insert(char *t)
     23 {
     24     node *rt=root;int m=strlen(t);
     25     for(int i=0;i<m;i++)
     26     {
     27         if(rt->mark)return;
     28         if(!rt->ch[t[i]-'a'])
     29         {
     30             rt->ch[t[i]-'a']=new node(++cnt);
     31             man[cnt]=rt->ch[t[i]-'a'];
     32         }
     33         rt=rt->ch[t[i]-'a'];
     34     }
     35     rt->mark=1;
     36 }
     37 inline void bfs()
     38 {
     39     root->f=NULL;q[++tl]=root;
     40     while(hd<=tl)
     41     {
     42         node *rt=q[hd++];
     43         for(int i=0;i<size;i++)
     44         {
     45             if(rt->ch[i])
     46             {
     47                 q[++tl]=rt->ch[i];
     48                 node *u=rt->f;
     49                 while(u&&!u->ch[i])u=u->f;
     50                 rt->ch[i]->f=(u)?u->ch[i]:root;
     51             }
     52             else
     53             {
     54                 node *u=rt->f;
     55                 rt->ch[i]=(u)?u->ch[i]:root->ch[i];
     56             }
     57         }
     58     }
     59 }
     60 inline void intn()
     61 {
     62     long double k=(long double)1/size;
     63     for(int i=0;i<=cnt;i++)
     64         if(!man[i]->mark)
     65             for(int j=0;j<size;j++)
     66                 if(man[i]->ch[j])
     67                     if(man[i]->ch[j]->mark)
     68                         A.m[i][cnt+1]+=k,A.m[i][0]+=k;
     69                     else
     70                         A.m[i][man[i]->ch[j]->id]+=k;
     71                 else
     72                     A.m[i][0]+=k;
     73     A.m[cnt+1][cnt+1]=1;
     74 }
     75 int main()
     76 {
     77     scanf("%d%d%d",&n,&len,&size);
     78     root=new node(0);
     79     man[0]=root;
     80     for(int i=1;i<=n;i++)
     81         scanf("%s",s[i]),insert(s[i]);
     82     bfs();intn();
     83     B.m[0][0]=1;
     84     while(len)
     85     {
     86         if(len&1)
     87         {
     88             tmp.clear();
     89             for(int k=0;k<=cnt+1;k++)
     90                 for(int j=0;j<=cnt+1;j++)
     91                     tmp.m[0][j]+=B.m[0][k]*A.m[k][j];
     92             for(int i=0;i<=cnt+1;i++)
     93                 for(int j=0;j<=cnt+1;j++)
     94                     B.m[i][j]=tmp.m[i][j];
     95         }
     96         len>>=1;    
     97         tmp.clear();
     98         for(int k=0;k<=cnt+1;k++)
     99             for(int i=0;i<=cnt+1;i++)
    100                 for(int j=0;j<=cnt+1;j++)
    101                     tmp.m[i][j]+=A.m[i][k]*A.m[k][j];
    102         for(int i=0;i<=cnt+1;i++)
    103             for(int j=0;j<=cnt+1;j++)
    104                 A.m[i][j]=tmp.m[i][j];
    105     }
    106     double ans=B.m[0][cnt+1];
    107     printf("%.9lf",ans);
    108 }
    BZOJ2553
     
  • 相关阅读:
    C#_数据访问类查询 和 属性扩展
    C#_0913 完整修改删除、实体类和数据访问类
    webform基础
    TreeView, 数据导出
    无边框窗体,tiner控件
    容器,打印设置
    多窗体,菜单工具栏
    公共控件
    winform
    数据访问类查询
  • 原文地址:https://www.cnblogs.com/LadyLex/p/7078117.html
Copyright © 2011-2022 走看看