题意:
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");
}*/
}