• #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 imes |a| imes |b|)^3)$ 过不去,我们需要优化一下

    可以发现 $f_{?,?,k}$ 只由 $f_{?,?,k}$ 和 $f_{?,?,k+1}$ 影响,而且有个很显然的结论就是 $f_{?,?,|b|}=f_{?,|a|,?}=0$。所以当我们要得到 $f_{?,?,k}$ 的答案时,我们只需要将 $f_{?,?,k+1}$ 的答案作为其对应常数项,然后在最后一位 $=k$ 时进行高斯消元即可,复杂度降至 $O((n imes |a|)^3 imes |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
    ",f[1][0][0]),0;
    }
  • 相关阅读:
    Mifare系列3-卡的能源和数据传递(转)
    Mifare系列2-非接触卡标准(转)
    Mifare系列1-简介(转)
    oot 空间不足解决方法
    C语言位操作(转)
    C语言面试题(三)
    C语言运算符和优先级
    C语言面试题(二)
    C语言面试题(一)
    Ubuntu+Win7双系统grub的修复问题
  • 原文地址:https://www.cnblogs.com/xjqxjq/p/10920191.html
Copyright © 2020-2023  润新知