传送吧
https://www.lydsy.com/JudgeOnline/problem.php?id=2553
思路
建出trie图。记f[i][j]为长度为i到节点j的期望。直接转移不行。
建出trie图的邻接矩阵,然后自乘len-1次转移。
具体就是如果当前点的儿子是禁忌点,就连向根(由儿子连长度多了1),否则连向儿子。这样就避免了重叠。而且有个显然的结论就是能走到禁忌点就走,肯定能取到最大值。
我们在每条边上放上概率,将值放在一个新建的点上,然后能走到禁忌点就连向它。不能放在根是显然的。转移就相当于i->k,k->j贡献i->j,就是在trie图上有方向的走(没方向考虑高消),然后统计答案。问的是从根走len步走到最终点的期望。
这道题告诉我们算期望可以先考虑概率,将值放在最后再乘。
long double是必要的,其他细节看代码。
代码
#include <bits/stdc++.h>
#define maxn 105
using namespace std;
int n, len, alp, cnt;
char x[maxn];
struct AC{
AC *son[26], *fail;
int ep, id;
void Clear(){
for(int i = 0; i < 26; i++) son[i] = NULL;
ep = 0;
id = cnt;
}
}Node[maxn], *Root, *q[maxn];
struct Mat{
int r, c;
long double A[maxn][maxn];
void Clear(){
memset(A, 0, sizeof(A));
}
}f;
AC *NewTnode(){
Node[cnt].Clear();
return Node+cnt++;
}
void Insert(char *s){
AC *now = Root;
for(; *s != '\0'; s++){
int pos = (*s) - 'a';
if(!now->son[pos]) now->son[pos] = NewTnode();
now = now->son[pos];
}
now->ep = 1;
}
void Build(){
int hh = 0, tt = 0;
q[hh] = Root;
Root->fail = NULL;
while(hh <= tt){
AC *now = q[hh++];
for(int i = 0; i < alp; i++){
if(now->son[i]){
q[++tt] = now->son[i];
now->son[i]->fail = (now == Root) ? Root : now->fail->son[i];
now->son[i]->ep |= now->son[i]->fail->ep;
}
else now->son[i] = (now == Root) ? Root : now->fail->son[i];
}
}
}
Mat operator * (Mat X, Mat Y){
Mat Z;
Z.r = X.r; Z.c = Y.c;
Z.Clear();
for(int i = 0; i <= X.r; i++)
for(int j = 0; j <= Y.c; j++)
for(int k = 0; k <= X.c; k++)
Z.A[i][j] = (Z.A[i][j] + X.A[i][k] * Y.A[k][j]);
return Z;
}
Mat Pow(Mat X){
Mat Z = X;
len --;
while(len){
if(len & 1) Z = Z * X;
X = X * X;
len >>= 1;
}
return Z;
}
int main(){
scanf("%d%d%d", &n, &len, &alp);
Root = NewTnode();
for(int i = 1; i <= n; i++){
scanf("%s", x);
Insert(x);
}
Build();
f.r = f.c = cnt;
f.Clear();
f.A[cnt][cnt] = 1.0;
long double possi = 1.0 / alp;
for(int i = 0; i < cnt; i++){
AC *now = Node+i;
for(int j = 0; j < alp; j++){
if(now->son[j]->ep){
f.A[i][0] += possi;
f.A[i][cnt] += possi;
continue;
}
f.A[i][now->son[j]->id] += possi;
}
}
f = Pow(f);
printf("%.7Lf\n", f.A[0][cnt]);
return 0;
}
What a good thing we lose
多么幸运 我们错失彼此
What a bad thing we knew
何其不幸 我们曾经相知