Gym 103119B Boring Problem

题目传送门

考虑建出所有 \(T\) 的 AC 自动机,那么添加一个字符等价于走一步。终止条件即为走到一个叶子节点。

\(E_u\) 表示从节点 \(u\) 走到叶子节点的期望步数,则有:

\(u\) 为叶子节点时,\(E_u=0\)

否则 \(E_u=1+\sum p_i\times E_{tr_{u,i}}\)

显然转移是会有环的,所以我们需要高斯消元,时间复杂度 \(O(n^3m^3)\),不能通过。

考虑一个事情,对于一个点 \(u\),设它有 \(t\) 个儿子,如果我们知道 \(E_u\) 以及其中 \(t-1\) 个儿子的 \(E\),我们就能知道另外一个儿子的 \(E\),即:

\[E_{tr_{u,x}}=\dfrac{E_u-1-\sum[i\ne x]p_i\times E_{tr_{u,i}}}{p_x} \]

先考虑我们的方程数量,由于只有叶子节点知道确切的 \(E\) 值,所以只有在叶子节点时我们才能列出方程,即方程个数为 \(n\)

再考虑未知元的数量,为分叉个数加 \(1\),即为 \(n-1+1=n\)

于是直接高斯消元即可。

AC code:

#include<bits/stdc++.h>
#define int long long
#define N 105
#define pii pair<int,int>
#define x first
#define y second
#define mod 1000000007
#define inf 2e18
using namespace std;
int T=1,n,m,k,inv,p[26],e[N*N][N],a[N][N],tot=1,cnt,res[N*N];
int ksm(int x,int y){
	int res=1;
	while(y){
		if(y&1)(res*=x)%=mod;
		(x*=x)%=mod;
		y>>=1;
	}
	return res;
}
struct acam{
	struct node{
		int s[26],flag,ne;
	}tr[N*N];
	int idx=1;
	void ins(string s){
		int p=1,siz=s.size();
		for(int i=0;i<siz;i++){
			int t=s[i]-'a';
			if(!tr[p].s[t])tr[p].s[t]=++idx;
			p=tr[p].s[t];
		}
		tr[p].flag=1;
	}
	void build(){
		for(int i=0;i<26;i++){
			tr[0].s[i]=1;
		}
		queue<int>q;
		q.push(1);
		while(!q.empty()){
			int u=q.front();
			q.pop();
			int ne=tr[u].ne;
			for(int i=0;i<26;i++){
				int v=tr[u].s[i];
				if(!v){
					tr[u].s[i]=tr[ne].s[i];
					continue;
				}
				tr[v].ne=tr[ne].s[i];
				q.push(v);
			}
		}
	}
}ac;
void init(){
	queue<int>q;
	e[1][0]=0;
	e[1][1]=1;
	q.push(1);
	while(!q.empty()){
		int u=q.front();
		q.pop();
		if(ac.tr[u].flag){
			cnt++;
			for(int i=0;i<=tot;i++){
				a[cnt][i]=e[u][i];
			}
			continue;
		}
		vector<int>son;
		int P=-1,id=0;
		for(int i=0;i<26;i++){
			int v=ac.tr[u].s[i];
			if(v!=ac.tr[ac.tr[u].ne].s[i]){
				q.push(v);
				son.push_back(v);
				if(P==-1){
					P=p[i];
					id=v;
				}
			}
		}
		for(int i=1;i<son.size();i++){
			e[son[i]][0]=0;
			e[son[i]][++tot]=1;
		}
		int pv=ksm(P,mod-2);
		e[id][0]=(e[u][0]+mod-1)*pv%mod;
		for(int i=1;i<=tot;i++){
			e[id][i]=e[u][i]*pv%mod;
		}
		for(int i=0;i<26;i++){
			int v=ac.tr[u].s[i],c=p[i]*pv%mod;
			if(v!=id){
				for(int j=0;j<=tot;j++){
					(e[id][j]+=mod-c*e[v][j]%mod)%=mod;
				}
			}
		}
	} 
	for(int i=1;i<=tot;i++){
		a[i][tot+1]=(mod-a[i][0])%mod;
	}
}
void gauss(int a[N][N],int n){
	for(int i=1;i<=n;i++){
		int r;
		for(int j=i;j<=n;j++){
			if(a[j][i]){
				r=j;
				break;
			}
		}
		if(r!=i)swap(a[r],a[i]);
		int t=ksm(a[i][i],mod-2);
		for(int j=i;j<=n+1;j++){
			(a[i][j]*=t)%=mod;
		}
		for(int k=1;k<=n;k++){
			if(k!=i&&a[k][i]){
				for(int j=n+1;j>=i;j--){
					(a[k][j]+=mod-a[i][j]*a[k][i]%mod)%=mod;
				}
			}
		}
	}
}
void calc(){
	for(int i=1;i<=ac.idx;i++){
		res[i]=e[i][0];
		for(int j=1;j<=tot;j++){
			(res[i]+=a[j][tot+1]*e[i][j]%mod)%=mod;
		}
	}
	res[0]=res[1];
}
void qry(string s){
	int p=1,flag=0,siz=s.size();
	for(int i=0;i<siz;i++){
		int t=s[i]-'a';
		p=ac.tr[p].s[t];
		if(ac.tr[p].flag)flag=1;
		cout<<(i+1+(1-flag)*res[p])%mod<<'\n';
	}
}
void solve(int cs){
	cin>>n>>m>>k;
	inv=ksm(100,mod-2);
	for(int i=0;i<k;i++){
		cin>>p[i];
		(p[i]*=inv)%=mod;
	}
	for(int i=1;i<=n;i++){
		string s;
		cin>>s;
		ac.ins(s);
	}
	string s;
	cin>>s;
	ac.build();
	init();
	gauss(a,tot);
	calc();
	qry(s);
}
signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
//	cin>>T;
//	init();
	for(int cs=1;cs<=T;cs++){
		solve(cs);
	}
	return 0;
}
posted @ 2025-04-15 17:28  zxh923  阅读(10)  评论(0)    收藏  举报