#4248. 走

题意

题目还是简单一点好。

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

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

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

对于所有数据, n ≤ 20 , ∣ a ∣ ≤ 10 , ∣ b ∣ ≤ 50 n ≤ 20,|a|≤ 10,|b|≤ 50 n20,a10,b50,期望步数是一个确定的有理数。

题解

首先设计 d p dp dp 状态

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

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

可以发现 f ? , ? , k f_{?,?,k} f?,?,k 只由 f ? , ? , k f_{?,?,k} f?,?,k f ? , ? , k + 1 f_{?,?,k+1} f?,?,k+1 影响,而且有个很显然的结论就是 f ? , ? , ∣ b ∣ = f ? , ∣ a ∣ , ? = 0 f_{?,?,|b|}=f_{?,|a|,?}=0 f?,?,b=f?,a,?=0。所以当我们要得到 f ? , ? , k f_{?,?,k} f?,?,k 的答案时,我们只需要将 f ? , ? , k + 1 f_{?,?,k+1} f?,?,k+1 的答案作为其对应常数项,然后在最后一位 = k =k =k 时进行高斯消元即可,复杂度降至 O ( ( n × ∣ a ∣ ) 3 × ∣ b ∣ ) O((n \times |a|)^3 \times |b|) O((n×a)3×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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值