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

    2553: [BeiJing2011]禁忌

    Time Limit: 20 Sec  Memory Limit: 128 MBSec  Special Judge
    Submit: 203  Solved: 75
    [Submit][Status]

    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

    第一行包含三个正整数Nlenalphabet

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

     

    Output

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

     

    Sample Input

    2 4 2

    aa

    abb

    Sample Output

    0.75

    【样例1解释】
    一共有2^4 = 16种不同的魔法。

    需要注意的是“aabb”的禁忌伤害是1而不是2。

    HINT

    100%的数据中N ≤ 5len ≤1091 ≤ alphabet ≤ 26

    在所有数据中,有不少于40%的数据中:N = 1

    数据保证每个串Ti的长度不超过15,并且不是空串。

    数据保证每个Ti均仅含有前alphabet个小写字母。

    数据保证集合T中没有相同的元素,即对任意不同的ij,有TiTj

    【评分方法】

    对于每一组数据,如果没有得到正确的输出(TLEMLERTE、输出格式错误等)得0分。

    否则:设你的输出是YourAns,标准输出是StdAns

    MaxEPS = max(1.0 , StdAns)×10-6

    如果|YourAns – StdAns| ≤ MaxEPS则得10分,否则得0分。


    即:你的答案需要保证相对误差或绝对误差不超过10-6

    题解:

    出题人果然丧心病狂卡精度。。。

    这题写题解的人好像不多,也许是我太傻叉了,时光机的代码画面太美我都不敢看了。。。

     首先我们先搞清一个问题,如果给定了一个字符串,那么它的伤害指数是多少。

    转化一下就变成 在一个数轴上给定若干条线段,请选出最多的线段并且使得这些线段两两交集为空。

    然后这就是一个贪心水题,见http://www.cnblogs.com/zyfzyf/p/4006703.html

    我们只要按右端点排序,能取就取。

    那假如我们已经构建了一个AC自动机,我们只要走到一个危险节点,就ans++,并且退回到根节点重新开始走。

    可以看出,这样正好相当于在模拟上面的贪心过程。

    然后我们看看len增加1,我们能干什么

    建图如下:

    void build()
    {
        vis[1]=1;
        q.push(1);long double tmp=1.0/k;
        while(!q.empty())
        {
            int x=q.front();q.pop();
            for0(i,k-1)
            {
                if(!vis[t[x][i]])vis[t[x][i]]=1,q.push(t[x][i]);
                if(v[t[x][i]])
                {
                    a.d[x][n]+=tmp;
                    a.d[x][1]+=tmp;
                }else a.d[x][t[x][i]]+=tmp;
            }
        }
    }

    意思就是我们把所有这样的关系找出来,如果len结束时,我们在x。

    那如果t[x][i]是根节点,那我们就有1.0/alp的期望使ans+1,并返回根节点,否则我们有1.0/alp的期望走到t[x][i]。

    最后我们求长度为给定是从1走到ans(设置为节点n)的期望次数即可。

    然后这个矩阵构建出来我们发现 i到j的期望就等于sigma(i到k的期望*k到j的期望)。

    这正好是矩阵乘法!

    然后我们就可用快速幂加速了。

    注意设置a[n][n]=1

    代码:

      1 #include<cstdio>
      2 
      3 #include<cstdlib>
      4 
      5 #include<cmath>
      6 
      7 #include<cstring>
      8 
      9 #include<algorithm>
     10 
     11 #include<iostream>
     12 
     13 #include<vector>
     14 
     15 #include<map>
     16 
     17 #include<set>
     18 
     19 #include<queue>
     20 
     21 #include<string>
     22 
     23 #define inf 1000000000
     24 
     25 #define maxn 200+5
     26 
     27 #define eps 1e-10
     28 
     29 #define ll long long
     30 
     31 #define pa pair<int,int>
     32 
     33 #define for0(i,n) for(int i=0;i<=(n);i++)
     34 
     35 #define for1(i,n) for(int i=1;i<=(n);i++)
     36 
     37 #define for2(i,x,y) for(int i=(x);i<=(y);i++)
     38 
     39 #define for3(i,x,y) for(int i=(x);i>=(y);i--)
     40 
     41 #define mod 1000000007
     42 
     43 using namespace std;
     44 
     45 inline int read()
     46 
     47 {
     48 
     49     int x=0,f=1;char ch=getchar();
     50 
     51     while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
     52 
     53     while(ch>='0'&&ch<='9'){x=10*x+ch-'0';ch=getchar();}
     54 
     55     return x*f;
     56 
     57 }
     58 int n,m,k,cnt,t[maxn][26],go[maxn];
     59 bool v[maxn],vis[maxn];
     60 queue<int>q;
     61 char s[maxn];
     62 struct matrix
     63 {
     64     long double d[maxn][maxn];
     65     matrix(){memset(d,0,sizeof(d));}
     66 }a,b,c;
     67 inline void add()
     68 {
     69     scanf("%s",s+1);int len=strlen(s+1),now=1;
     70     for1(i,len)
     71     {
     72         int x=s[i]-'a';
     73         if(!t[now][x])t[now][x]=++cnt;
     74         now=t[now][x];
     75     }
     76     v[now]=1;
     77 }
     78 void bfs()
     79 {
     80     q.push(1);
     81     while(!q.empty())
     82     {
     83         int x=q.front(),y,j;q.pop();
     84         v[x]|=v[go[x]];
     85         for0(i,k-1)
     86         {
     87             j=go[x];
     88             while(j&&!t[j][i])j=go[j];
     89             if(t[x][i])
     90             {
     91                 go[y=t[x][i]]=j?t[j][i]:1;
     92                 q.push(y);
     93             }else t[x][i]=j?t[j][i]:1;
     94         }
     95     }
     96 }    
     97 void build()
     98 {
     99     vis[1]=1;
    100     q.push(1);long double tmp=1.0/k;
    101     while(!q.empty())
    102     {
    103         int x=q.front();q.pop();
    104         for0(i,k-1)
    105         {
    106             if(!vis[t[x][i]])vis[t[x][i]]=1,q.push(t[x][i]);
    107             if(v[t[x][i]])
    108             {
    109                 a.d[x][n]+=tmp;
    110                 a.d[x][1]+=tmp;
    111             }else a.d[x][t[x][i]]+=tmp;
    112         }
    113     }
    114 }
    115 inline matrix operator *(matrix &x,matrix &y)
    116 {
    117     matrix z;
    118     for1(i,n)
    119      for1(j,n)
    120       for1(l,n)
    121        z.d[i][j]+=x.d[i][l]*y.d[l][j];
    122     return z;
    123 }
    124 void ksm(int cs)
    125 {
    126     for(;cs;cs>>=1,a=a*a)
    127         if(cs&1)b=b*a;
    128 }
    129 void printb()
    130 {
    131     for1(i,n)for1(j,n)cout<<i<<' '<<j<<' '<<b.d[i][j]<<endl;
    132 }
    133 void printa()
    134 {
    135     for1(i,n)for1(j,n)cout<<i<<' '<<j<<' '<<a.d[i][j]<<endl;
    136 }
    137 
    138 int main()
    139 
    140 {
    141 
    142     freopen("input.txt","r",stdin);
    143 
    144     freopen("output.txt","w",stdout);
    145 
    146     n=read();m=read();k=read();cnt=1;
    147     for1(i,n)add();
    148     bfs();
    149     n=cnt+1;
    150     build();
    151     for1(i,n)b.d[i][i]=1;
    152     a.d[n][n]=1;
    153     ksm(m);
    154     printf("%.7f
    ",(double)b.d[1][n]);
    155 
    156     return 0;
    157 
    158 }  
    View Code

     真是一道综合性的难题+好题!

    UPD:以上纯属口胡。。。

    一个边权为1的邻接矩阵自乘n次,则a[s][t]代表从s恰好经过n条边到t的路径条数。

    根据这一点我们可以推广

    令a[i][j]表示一步从i到j的期望,那么a自乘n次就a[s][t]就代表从s恰好经过n条边到达t的期望。

    然后我们新建了一给点n=cnt+1

    然后我们要求sigma(从1到n恰好经过j条边的期望)j<=len

    所以我们要设值a[n][n]=1,因为下一次计算的时候b[1][n]=。。。+b[1][n]*a[n][n]+。。。就可以把上一次的答案累计。

    就算是我懂了QAQ

  • 相关阅读:
    照片墙效果一多实例演示【已封装】
    把javascript event事件封装了下,兼容大多数浏览器
    catch error
    call tcl from c
    scrollbar
    sharedlibextension
    treectrl
    get file name part
    namespace eval
    glob
  • 原文地址:https://www.cnblogs.com/zyfzyf/p/4153860.html
Copyright © 2011-2022 走看看