#4248. 走

题意
题目还是简单一点好。

有一张图,每一个点有至少一条出边,每条边上有一个小写字母。有一只大象在图上走路,一开始在 $1$ 号点,之后每一步会随机选择该点的一条出边并走过去。

将大象走过的边上的小写字母顺次相连,就构成了一个字符串。大象有两个串 $a$ 和 $b$,如果大象走出的串包含 $a$ 作为子串或包含 $b$ 作为子序列,大象就会很生气,停止行走。

大象想知道在停止行走前大象期望会走几步。输入保证这个值是一个有理数,你只要输出它 $\mod 998244353$ 的值即可。

对于所有数据,$n ≤ 20,|a|≤ 10,|b|≤ 50$,期望步数是一个确定的有理数。
题解
首先设计 $dp$ 状态

$f_{i,j,k}$ 表示当前在 $i$ 点, $a$ 串匹配到了第 $j$ 位, $b$ 串匹配到了第 $k$ 位后还需要走的期望步数

设 $v$ 是 $i$ 连出的边, $d_i$ 是 $i$ 的出度, $w$ 是 $i$ 到 $v$ 边上的字符,则可以得到 $f_{i,j,k}=\frac{1}{d_i}\sum f[v][nex[j][w]][k+(b[k+1]==w)]+1$ ,可能有环所以应该要高斯消元,这样效率是 $O((n \times |a| \times |b|)^3)$ 过不去,我们需要优化一下

可以发现 $f_{?,?,k}$ 只由 $f_{?,?,k}$ 和 $f_{?,?,k+1}$ 影响,而且有个很显然的结论就是 $f_{?,?,|b|}=f_{?,|a|,?}=0$。所以当我们要得到 $f_{?,?,k}$ 的答案时,我们只需要将 $f_{?,?,k+1}$ 的答案作为其对应常数项,然后在最后一位 $=k$ 时进行高斯消元即可,复杂度降至 $O((n \times |a|)^3 \times |b|)$,可以过了

懒得上代码

#include <bits/stdc++.h>
using namespace std;
const int N=55,M=65005,Z=205,P=998244353;
int n,m,d[N],a[N],A,b[N],B,hd[N],W[M],V[M],nx[M],t;
int ne[N][N],id[Z][Z],c,g[Z][Z],f[N][N][N];char sr[N];
int X(int x){if (x>=P) x-=P;if (x<0) x+=P;return x;}
int K(int x,int y){
    int z=1;
    for (;y;y>>=1,x=1ll*x*x%P)
        if (y&1) z=1ll*z*x%P;
    return z;
}
void add(int u,int v,int w){
    nx[++t]=hd[u];W[t]=w;
    V[hd[u]=t]=v;d[u]++;
}
bool J(int x,int y){
    for (;x;x--,y--)
        if (a[x]!=a[y]) return 0;
    return 1;
}
void Gauss(){
    for (int v,i=1;i<=c;i++){
        if (!g[i][i]){
            int k=i;
            for (int j=i+1;j<=c;j++)
                if (g[j][i]){k=j;break;}
            swap(g[i],g[k]);
        }
        if (!g[i][i]) continue;
        v=K(g[i][i],P-2);
        for (int j=i;j<=c;j++)
            g[i][j]=1ll*g[i][j]*v%P;
        g[i][0]=1ll*g[i][0]*v%P;
        for (int l,j=1;j<=c;j++)
            if (i!=j && g[j][i]){
                l=g[j][i];
                for (int k=i;k<=c;k++)
                    g[j][k]=X(g[j][k]-1ll*l*g[i][k]%P);
                g[j][0]=X(g[j][0]-1ll*l*g[i][0]%P);
            }
    }
}
int main(){
    scanf("%d%d",&n,&m);;
    for (int u,v,i=1;i<=m;i++)
        scanf("%d%d%s",&u,&v,sr),
        add(u,v,sr[0]-'a');
    scanf("%s",sr+1);A=strlen(sr+1);
    for (int i=1;i<=A;i++) a[i]=sr[i]-'a';
    scanf("%s",sr+1);B=strlen(sr+1);
    for (int i=1;i<=B;i++) b[i]=sr[i]-'a';
    for (int i=0;i<A;i++)
        for (int j=0;j<26;j++)
            for (int k=i;~k;k--)
                if (a[k+1]==j && J(k,i))
                    {ne[i][j]=k+1;break;}
    for (int i=1;i<=n;i++)
        for (int j=0;j<A;j++) id[i][j]=++c;
    for (int k=B-1;~k;k--){
        for (int i=0;i<=c;i++)
            for (int j=0;j<=c;j++) g[i][j]=0;
        for (int x,i=1;i<=n;i++)
            for (int j=0;j<A;j++)
                x=id[i][j],g[x][0]=g[x][x]=d[i];
        for (int i=1;i<=n;i++)
            for (int y,j=0;j<A;j++){
                y=id[i][j];
                for (int v,o,x=hd[i];x;x=nx[x]){
                    v=V[x];o=ne[j][W[x]];
                    if (W[x]==b[k+1])
                        g[y][0]=X(g[y][0]+f[v][o][k+1]);
                    else if (o<A) g[y][id[v][o]]=X(g[y][id[v][o]]-1);
                }
            }
        Gauss();
        for (int i=1;i<=n;i++)
            for (int j=0;j<A;j++)
                f[i][j][k]=g[id[i][j]][0];
    }
    return printf("%d\n",f[1][0][0]),0;
}

 

转载于:https://www.cnblogs.com/xjqxjq/p/10920191.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值