链接
思路
对
n
n
n 个串建出AC自动机,那么每个人获胜的概率就是到每个串末尾结点的概率。直接设概率难以推导,设经过点
i
i
i 的期望次数是
e
i
e_i
ei,由于游戏只进行一轮,经过末尾结点的期望和概率是相等的。
如果
i
i
i 不是根结点,那么
e
i
e_i
ei 就是所有经过
i
i
i 结点的前驱的期望之和的
1
6
\frac{1}{6}
61。对于根结点,初始情况下期望是
1
1
1。
代码
#include <bits/stdc++.h>
using namespace std;
template <typename T>
inline void read(T &x)
{
T data = 0, f = 1;
char ch = getchar();
while (!isdigit(ch))
{
if (ch == '-')
f = -1;
ch = getchar();
}
while (isdigit(ch))
{
data = (data << 3) + (data << 1) + ch - '0';
ch = getchar();
}
x = f * data;
}
template <typename T, typename... Args>
inline void read(T &t, Args &...args)
{
read(t);
read(args...);
}
const int inf = 0x3f3f3f3f;
const double eps = 1e-8;
const int maxn = 1e5 + 9;
const int MAXN = 220;
int n, l;
double a[MAXN][MAXN], x[MAXN]; // 方程的左边的矩阵和等式右边的值,求解之后x存的就是结果
int equ, var; // 方程数和未知数个数
struct tnode
{
int ch[6];
int cnt, fail;
};
tnode tr[maxn];
int tot, ans[maxn];
void ins(int k)
{
int u = 0;
for (int i = 1, x; i <= l; ++i)
{
read(x);
x--;
if (!tr[u].ch[x])
tr[u].ch[x] = ++tot;
u = tr[u].ch[x];
}
tr[u].cnt = k;
ans[k] = u;
}
void build()
{
queue<int> q;
for (int i = 0; i < 6; ++i)
{
if (tr[0].ch[i])
{
tr[tr[0].ch[i]].fail = 0;
q.push(tr[0].ch[i]);
}
}
while (!q.empty())
{
int x = q.front();
q.pop();
for (int i = 0; i < 6; ++i)
{
if (tr[x].ch[i])
{
tr[tr[x].ch[i]].fail = tr[tr[x].fail].ch[i];
q.push(tr[x].ch[i]);
}
else
{
tr[x].ch[i] = tr[tr[x].fail].ch[i];
}
}
}
}
void init()
{
var = tot + 1, equ = tot + 1;
for (int i = 0; i <= tot; ++i)
{
if (!tr[i].cnt)
{
for (int j = 0; j < 6; ++j)
a[tr[i].ch[j]][i] += (1.0 / 6);
}
a[i][i] += -1;
}
x[0] += -1;
}
int Gauss()
{
int i, j, k, col, max_r;
for (k = 0, col = 0; k < equ && col < var; k++, col++)
{
max_r = k;
for (i = k + 1; i < equ; i++)
if (fabs(a[i][col]) > fabs(a[max_r][col]))
max_r = i;
if (fabs(a[max_r][col]) < eps)
return 0;
if (k != max_r)
{
for (j = col; j < var; j++)
swap(a[k][j], a[max_r][j]);
swap(x[k], x[max_r]);
}
x[k] /= a[k][col];
for (j = col + 1; j < var; j++)
a[k][j] /= a[k][col];
a[k][col] = 1;
for (i = 0; i < equ; i++)
if (i != k)
{
x[i] -= x[k] * a[i][k];
for (j = col + 1; j < var; j++)
a[i][j] -= a[k][j] * a[i][col];
a[i][col] = 0;
}
}
return 1;
}
void reset()
{
for (int i = 0; i <= tot; ++i)
{
memset(tr[i].ch, 0, sizeof(tr[i].ch));
tr[i].cnt = tr[i].fail = 0;
}
memset(x, 0, sizeof(x));
memset(a, 0, sizeof(a));
tot = 0;
}
int main(int argc, char const *argv[])
{
int T;
read(T);
while (T--)
{
read(n, l);
for (int i = 1; i <= n; ++i)
ins(i);
build();
init();
Gauss();
for (int i = 1; i <= n; ++i)
printf("%.6lf%c", x[ans[i]], (i == n ? '\n' : ' '));
reset();
}
return 0;
}
这题还有两个加强版:
P6125 [JSOI2009]有趣的游戏
P3706 [SDOI2017]硬币游戏