bzoj 2553: [BeiJing2011]禁忌

9 篇文章 0 订阅
6 篇文章 0 订阅

题意:

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使用对方的每一个魔法的概率是均等的,那么每一次随机使用魔法所受到的禁忌伤害的期望值是多少。

题解:

挺好的题 。
首先大致思路一眼秒,AC自动机+期望dp+矩乘优化。
然而因为太菜,所以暴力期望dp弄了很久……
其实加多一个虚点,计算到那个点的期望次数就是伤害,那么就很好转移了。
然后矩乘。
我才不会说我卡精不想调精A
code:

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
struct trnode{
    int a[27],fail;
}tr[80];int root=1,tot=1;
bool check[80];
char s[30];
int n,m,al;
struct node{
    long double a[80][80];
    node() {memset(a,0,sizeof(a));}
    int n,m;
};
node operator * (node a,node b)
{
    node ans;//memset(a,0,sizeof(a));
    ans.n=a.n;ans.m=b.m;
    for(int i=0;i<=ans.n;i++)
        for(int j=0;j<=ans.m;j++)
            for(int k=0;k<=a.m;k++)
                ans.a[i][j]+=a.a[i][k]*b.a[k][j];
    return ans;
}
node operator ^ (node a,int b)
{
    node ans;ans.n=a.n;ans.m=a.m;
    for(int i=0;i<=a.n;i++) ans.a[i][i]=1.0;
    while(b)
    {
        if(b&1) ans=ans*a;
        a=a*a;b>>=1;
    }
    return ans;
}
void build()
{
    int len=strlen(s+1),x=root;
    for(int i=1;i<=len;i++)
    {
        int c=s[i]-'a'+1;
        if(!tr[x].a[c]) tr[x].a[c]=++tot;
        x=tr[x].a[c];
    }
    check[x]=true;
}
queue<int> q;
void make_fail()
{
    q.push(root);
    while(!q.empty())
    {
        int x=q.front();q.pop();
        for(int i=1;i<=al;i++)
        {
            if(!tr[x].a[i]) continue;
            if(x==root) tr[tr[x].a[i]].fail=root;
            else
            {
                int j=tr[x].fail;
                while(j!=root&&!tr[j].a[i]) j=tr[j].fail;
                tr[tr[x].a[i]].fail=tr[j].a[i];
                check[tr[x].a[i]]|=check[tr[j].a[i]];
            }
            q.push(tr[x].a[i]);
        }
    }
}
/*double ans;
void dp(int l)
{
    for(int i=1;i<=tot;i++)
    {
        if(vis[l-1][i]!=tim) continue;
        for(int c=1;c<=al;c++)
        {
            int k=i;
            while(k!=root&&!tr[k].a[c]) k=tr[k].fail;
            int x=tr[k].a[c];
            if(check[x])
                x=root,f[l][x]+=f[l-1][i]/al,ans+=f[l-1][i]/al;
            else f[l][x]+=f[l-1][i]/al;
            vis[l][x]=tim+1;
        }
    }
}*/
int main()
{
    scanf("%d %d %d",&n,&m,&al);
    for(int i=1;i<=al;i++) tr[root].a[i]=++tot;
    memset(check,false,sizeof(check));
    for(int i=1;i<=n;i++) scanf("%s",s+1),build();
    make_fail();
    node f,a,ans;
    f.n=1;f.m=tot;f.a[0][1]=1.0;
    a.n=a.m=tot;a.a[0][0]=1.0;
    for(int i=1;i<=tot;i++)
        if(!check[i])
        {
            long double tmp=(1.0/al);
            for(int j=1;j<=al;j++)
            {
                int k=i;
                while(k!=root&&!tr[k].a[j]) k=tr[k].fail;
                int x=tr[k].a[j];
                if(check[x]) a.a[i][root]+=tmp,a.a[i][0]+=tmp;
                else a.a[i][x]+=tmp;
            }
        }
    f=f*(a^m);
    printf("%.9lf",(double)f.a[0][0]);
    //printf("nan");
    /*vis[0][root]=1;ans=0.0;
    f[0][root]=1;
    //printf("%.5lf\n",p);
    for(int i=1;i<=m;i++)
    {
        dp(i);tim++;
        for(int j=1;j<=tot;j++) printf("%Lf ",f[i][j]);printf("\n");
    }*/
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值