题目链接
n个人每人选择一个长度为m的序列,序列中每个元素是1到6中的整数。每次投骰子等概率的得到1到6的的一个点数。当点数序列最后m位与任何一个人的序列匹配时停止,且该人获胜,问每个人获胜的概率。
以所有个人的序列构造fail树,那么每个节点有一个编号,且从该点走下一步共有6中情况,每种情况的概率是1/6。当走到终点时,没有下一步,因为游戏已经结束。
假设一共有idx+1(虚拟源点)个点。设走到i的总概率为xi,则 a 0 x 0 + a 1 x 1 + . . . + a i d x x i d x = x i a_0x_0+a_1x_1+...+a_{idx}x_{idx}=x_i a0x0+a1x1+...+aidxxidx=xi(aj为从j点走向i点的概率)。除了虚拟源点外,任何点都不能走向自己,因此上式中 a i = 0 a_i=0 ai=0,将 x i x_i xi移至等式左边, x i x_i xi的系数就是-1,即矩阵中g[i][i]=-1。
对于超级源点(编号为0),初始概率为1.设其他点对它的贡献为
a
0
x
0
+
a
1
x
1
+
.
.
.
+
a
i
d
x
x
i
d
x
=
x
0
−
1
a_0x_0+a_1x_1+...+a_{idx}x_{idx}=x_0-1
a0x0+a1x1+...+aidxxidx=x0−1 (总概率-初始概率)
移项后a0-1(与其他点一致)等式右边剩下-1
因此最终得到的矩阵如下:
0:
a
0
a_0
a0
a
1
a_1
a1
a
2
a_2
a2
a
3
a_3
a3 …
a
i
d
x
a_{idx}
aidx -1
1:
a
0
a_0
a0
a
1
a_1
a1
a
2
a_2
a2
a
3
a_3
a3 …
a
i
d
x
a_{idx}
aidx 0
2:
a
0
a_0
a0
a
1
a_1
a1
a
2
a_2
a2
a
3
a_3
a3 …
a
i
d
x
a_{idx}
aidx 0
.
.
.
idx:
a
0
a_0
a0
a
1
a_1
a1
a
2
a_2
a2
a
3
a_3
a3 …
a
i
d
x
a_{idx}
aidx 0
高斯消元后求得每个点的概率
#include <bits/stdc++.h>
using namespace std;
const int N = 110;
const double eps = 1e-8;
int tr[N][7], n, m, idx;
int ne[N], a[N];
double g[N][N];
bool isend[N];
int id[N], q[N];
int sgn(double a, double b) {
if (fabs(a - b) < eps) return 0;
if (a > b) return 1;
else return -1;
}
void ins(int x) {
int p = 0;
for (int i = 1; i <= m; i++) {
int t = a[i];
if (!tr[p][t]) tr[p][t] = ++ idx;
p = tr[p][t];
}
id[x] = p;
isend[p] = 1;
}
void build() {
int hh = 0, tt = -1;
int cnt = 0;
for (int i = 1; i <= 6; i ++)
if (tr[0][i]) q[++ tt] = tr[0][i], g[tr[0][i]][0] += 1.0/6;
while (hh <= tt) {
int t = q[hh ++];
if (isend[t]) continue;
for (int i = 1; i <= 6; i ++) {
int &p = tr[t][i];
if (p) {
q[++ tt] = p;
ne[p] = tr[ne[t]][i];
}
else p = tr[ne[t]][i];
g[p][t] += 1.0/6;
}
}
}
void guass() {
int r = 0, c = 0;
for (; c <= idx; c ++) {
int t = r;
for (int i = t; i <= idx; i ++)
if (fabs(g[i][c]) > fabs(g[t][c]))t = i;
if (sgn(g[t][c], 0) == 0) continue;
swap(g[t], g[r]);
for (int i = idx + 1; i >= c; i --) g[r][i] /= g[r][c];
for (int i = r + 1; i <= idx; i ++) {
if (sgn(g[i][c], 0) != 0)
for (int j = idx + 1; j >= c; j --)
g[i][j] -= g[r][j] * g[i][c];
}
r ++;
}
for (int i = idx; i >= 0; i --) {
for (int j = i + 1; j <= idx; j ++)
g[i][idx + 1] -= g[i][j] * g[j][idx + 1];
}
}
int main() {
int T;
scanf("%d", &T);
while (T --) {
idx = 0;
memset(tr, 0, sizeof tr);
memset(ne, 0, sizeof ne);
memset(g, 0, sizeof g);
memset(isend, 0, sizeof isend);
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i ++) {
for (int j = 1; j <= m; j ++) scanf("%d", &a[j]);
ins(i);
}
for (int i = 0; i <= idx; i ++)
g[i][i] += -1;//第i个式子移项后xi的系数为-1
g[0][idx + 1] = -1;
build();
guass();
double sum = 0;
for (int i = 1; i <= n; i ++) sum += g[id[i]][idx + 1];
for (int i = 1; i < n; i ++) printf("%.6f ", g[id[i]][idx + 1] / sum);
printf("%.6f", g[id[n]][idx + 1] / sum);
puts("");
}
return 0;
}