题面
http://darkbzoj.tk/problem/1444
题解
广为人知的图上随机游走模型(在这里,“图”指的是$trie$图)
终止节点向自己连“黑洞边”。
设计一个$dp$,$f[i][j]$表示$i$点在第$j$次轮游走中的期望拥有的人数/一个人在第$j$轮游走走到这里的概率(概率即期望)
用矩阵快速幂加速,向量$[0,0,0,...0,1,0,...,0,0]$乘这个矩阵的$INF$次方就可以知道答案。
可以想象到,最后时刻,只有终止节点有概率,其他的地方概率是$0$。
所以直接高斯消元,用第$1000000007$次到$1000000008$次的转移列方程就行了。($107$是我的$INF$)
#include<iostream> #include<cstdio> #include<queue> #define ri register int using namespace std; struct node{ int son[26],fail,last; } t[500]; int tot,n,m,l,pos[20]; char ch[50]; double g[200][200],p[50]; void insert(char *s,int id) { int u=0; for (ri i=1;i<=l;i++) { if (!t[u].son[s[i]-65]) t[u].son[s[i]-65]=++tot; u=t[u].son[s[i]-65]; } t[u].last=id; pos[id]=u; } void build() { queue<int> q; for (ri i=0;i<m;i++) if (t[0].son[i]) q.push(t[0].son[i]); while (!q.empty()) { int u=q.front(); q.pop(); for (ri i=0;i<m;i++) { if (t[u].son[i]) t[t[u].son[i]].fail=t[t[u].fail].son[i],q.push(t[u].son[i]); else t[u].son[i]=t[t[u].fail].son[i]; t[u].last|=t[t[u].fail].last; } } } void Gauss() { for (ri i=0;i<=tot;i++) { int p=0; for (ri j=i;j<=tot;j++) if (g[j][i]) {p=j;break;} for (ri j=0;j<=tot+1;j++) swap(g[i][j],g[p][j]); double t=g[i][i]; for (ri j=0;j<=tot+1;j++) g[i][j]/=t; for (ri j=i+1;j<=tot;j++) { double d=g[j][i]; for (ri k=0;k<=tot+1;++k) g[j][k]-=g[i][k]*d; } } for (ri i=tot;i>=0;i--) { g[i][tot+1]/=g[i][i]; for (ri j=i-1;j>=0;j--) g[j][tot+1]-=g[j][i]*g[i][tot+1]; if (!g[i][tot+1]) g[i][tot+1]=0; } } int main(){ scanf("%d %d %d",&n,&l,&m); for (ri i=0;i<m;i++) { int a,b; scanf("%d %d",&a,&b); p[i]=1.0*a/b; } for (ri i=1;i<=n;i++) { scanf("%s",ch+1); insert(ch,i); } build(); for (ri i=0;i<=tot;i++) for (ri j=0;j<m;j++) if (!t[i].last) g[t[i].son[j]][i]+=p[j]; for (ri i=0;i<=tot;i++) g[i][i]--; for (ri i=0;i<=tot;i++) if (t[i].last) g[0][i]=1; else g[0][i]=0; g[0][tot+1]=1; Gauss(); for (ri i=1;i<=n;i++) printf("%.2lf\n",g[pos[i]][tot+1]); }