dtoj#4248. 走

Posted jessie-

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了dtoj#4248. 走相关的知识,希望对你有一定的参考价值。

题目描述:

题目还是简单一点好。

有一张图,每一个点有至少一条出边,每条边上有一个小写字母。有一只大象在图上走路,一开始 在 $1$ 号点,之后每一步会随机选择该点的一条出边并走过去。
将大象走过的边上的小写字母顺次相连,就构成了一个字符串。大象有两个串 $a$ 和 $b$,如果大象走 出的串包含 $a$ 作为 子串或包含 $b$ 作为子序列,大象就会很生气,停止行走。

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

思路:

对于会有正推会有无穷无尽的局面的概率期望问题,大多列出等式消元求解。

考虑 $f[x][j][k]$ 表示在点 $x$ 上,当前走过的字符匹配到 $a$ 串比配到 $j$ ,匹配 $b$ 串匹配到 $k$ 的走过的步数的期望。

有一个发现是答案在 $ f[?][?][|b|] $ 时答案为 $0$ ,可以考虑根据这个列出一个方程式,高斯消元,但是效率是 $O((abn)^{3})$

但是我们发现最后一位一定是递增的,所以考虑每次制作 $k,k+1$ 的方程式,并高斯消元,最后答案是 $f[1][0][0]$ 。

转移式:
$$
f[x][j][k]=sum ((f[to[i]][ne[j][w[i]]][k/k+1]+1) imes frac{1}{out[x]})
$$
$ne[j][ch]?$ 是提前预处理出对于 $a?$ 串,如果匹配到第 $j?$ 位下一位是 $ch?$ ,那么下一次可以匹配到哪里。

如果走过的边上的字符恰好满足为 $b[k+1]?$ 时, 对应最后一维为 $k+1?$ 的 $f?$ 数组,即增加一个常数。

否则则对左边对应项的系数造成改变。

 以下代码:

技术图片
#include<bits/stdc++.h>
#define il inline
#define LL long long
#define _(d) while(d(isdigit(ch=getchar())))
using namespace std;
const int N=25,M=405,p=998244353;
char s[55];
int a[12],b[55],l1,l2,A[205][205],ne[N][28],f[N][12][55];
int n,m,head[N],nex[M],to[M],cnt,w[M],out[N],id[N][12];
il int read(){
    int x,f=1;char ch;
    _(!)ch==-?f=-1:f;x=ch^48;
    _()x=(x<<1)+(x<<3)+(ch^48);
    return f*x;
}
il void insert(int x,int y,int z){
    nex[++cnt]=head[x];head[x]=cnt;
    to[cnt]=y;w[cnt]=z;out[x]++;
}
il int mu(int x,int y){
    return x+y>=p?x+y-p:x+y;
}
il int ksm(LL a,int y){
    LL b=1;
    while(y){
        if(y&1)b=b*a%p;
        a=a*a%p;y>>=1;
    }
    return b;
}
il bool pd(int x,int y){
    if(x==y)return 1;if(x==0)return 1;
    for(;x;x--,y--){
        if(a[x]!=a[y])return 0;
    }
    return 1;
}
il void Gauss(int n){
    for(int i=1,inv;i<=n;i++){
        if(!A[i][i]){
            int mx=i;
            for(int j=i+1;j<=n;j++)if(A[j][i]>A[mx][i])mx=j;
            if(i^mx)for(int j=0;j<=n;j++)swap(A[i][j],A[mx][j]);
        }
        if(!A[i][i])continue;inv=ksm(A[i][i],p-2);
        A[i][i]=1;A[i][0]=1ll*A[i][0]*inv%p;
        for(int j=i+1;j<=n;j++)A[i][j]=1ll*A[i][j]*inv%p;
        for(int j=1;j<=n;j++){
            if(i^j&&A[j][i]){
                int g=A[j][i];A[j][0]=mu(A[j][0],p-1ll*g*A[i][0]%p);
                for(int k=i;k<=n;k++)A[j][k]=mu(A[j][k],p-1ll*g*A[i][k]%p);
                
            }
        }
    }
}
int main()
{
    n=read();m=read();
    for(int i=1;i<=m;i++){
        int x=read(),y=read();scanf(" %s",s);
        insert(x,y,s[0]-a);
    }
    scanf(" %s",s+1);l1=strlen(s+1);
    for(int i=1;i<=l1;i++)a[i]=s[i]-a;
    scanf(" %s",s+1);l2=strlen(s+1);
    for(int i=1;i<=l2;i++)b[i]=s[i]-a;
    for(int i=0;i<l1;i++)for(int j=0;j<26;j++)
        for(int k=i;k>=0;k--)if(a[k+1]==j&&pd(k,i)){
            ne[i][j]=k+1;break;
        }
    int tot=0;
    for(int i=1;i<=n;i++)for(int j=0;j<l1;j++)id[i][j]=++tot;
    for(int l=l2-1,x;l>=0;l--){
        for(int i=1;i<=n;i++)for(int j=0;j<l1;j++)
            x=id[i][j],A[x][0]=A[x][x]=out[i];
        for(int i=1;i<=n;i++)for(int j=0;j<l1;j++){
            x=id[i][j];
            for(int k=head[i];k;k=nex[k]){
                int v=to[k],lj=ne[j][w[k]],lk=l+(w[k]==b[l+1]);
                if(w[k]==b[l+1]||lj==l1)
                    A[x][0]=mu(A[x][0],f[v][lj][lk]);
                else A[x][id[v][lj]]=mu(A[x][id[v][lj]],p-1);
            }
        }
        Gauss(tot);
        for(int i=1;i<=n;i++)for(int j=0;j<l1;j++)f[i][j][l]=A[id[i][j]][0];
        for(int i=0;i<=tot;i++)for(int j=0;j<=tot;j++)A[i][j]=0;
    }
    printf("%d
",f[1][0][0]);
    return 0;
}
View Code

 

以上是关于dtoj#4248. 走的主要内容,如果未能解决你的问题,请参考以下文章

[DTOJ3996]:Lesson5!(DP+拓扑+线段树)

底部的android寻呼机标签条带走整个片段

DTOJ2702:余数

DTOJ2704:数字互换

DTOJ2700:hello world

2018.12.16-dtoj-1166-不相同的子串的个数