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

  • 相关阅读:
    PointToPointNetDevice doesn't support TapBridgeHelper
    NS3系列—10———NS3 NodeContainer
    NS3系列—9———NS3 IP首部校验和
    NS3系列—8———NS3编译运行
    【习题 7-6 UVA
    【Good Bye 2017 C】 New Year and Curling
    【Good Bye 2017 B】 New Year and Buggy Bot
    【Good Bye 2017 A】New Year and Counting Cards
    【Educational Codeforces Round 35 D】Inversion Counting
    【Educational Codeforces Round 35 C】Two Cakes
  • 原文地址:https://www.cnblogs.com/zyfzyf/p/4153860.html
Copyright © 2011-2022 走看看