这题把AC自动机模板和高斯消元的经典题拼在了一起。
AC自动机请转HDU 2222。
高斯消元原型题请参考《挑战程序设计竞赛》第二版 288 页的 Random Walk。
有了这两题的基础之后,这一题只是把 Random Walk 题中的网格图转到了AC自动机的状态表里。
我们先将每个人的猜测序列建成AC自动机。对于AC自动机里的每个状态,如果该状态不是结束状态,则下一个骰子扔出1~6的时候,该状态的概率都会有1/6贡献到S(now, i)里,now表示该状态,S(i, j)表示状态i扔出j时转移到的下一个状态。初始化的时候,AC自动机节点0(即总的根节点)概率为1。这样生成一个sz行sz+1列的增广矩阵之后,拿高斯消元求解最右边一列的答案即可。
代码如下:
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <cmath>
using namespace std;
const int maxn=105;
int T,n,L,a[15],id[maxn];
struct Aho {
struct state {
int next[6],tag,fail;
} stateTable[maxn];
int size;
void init() {
memset(stateTable,0,sizeof stateTable);
size=0;
}
void insert(int *a,int x) {
int now=0;
for (int i=0;i<L;++i) {
if (!stateTable[now].next[a[i]-1])
stateTable[now].next[a[i]-1]=++size;
now=stateTable[now].next[a[i]-1];
}
stateTable[now].tag=1;
id[x]=now;
}
void build() {
queue<int> que;
stateTable[0].fail=-1;
for (int i=0;i<6;++i)
if (stateTable[0].next[i]) {
stateTable[stateTable[0].next[i]].fail=0;
que.push(stateTable[0].next[i]);
}
while (!que.empty()) {
int now=que.front();
que.pop();
for (int i=0;i<6;++i) {
int o=stateTable[now].next[i];
if (o) {
int v=stateTable[now].fail;
while (v!=-1) {
if (stateTable[v].next[i]) {
stateTable[o].fail=stateTable[v].next[i];
break;
}
v=stateTable[v].fail;
}
if (v==-1)
stateTable[o].fail=0;
que.push(o);
}
}
}
}
int getnext(int i,int j) {
int v=stateTable[i].next[j];
if (!v) {
int o=stateTable[i].fail;
while (o!=-1) {
if (stateTable[o].next[j])
return stateTable[o].next[j];
o=stateTable[o].fail;
}
}
return v;
}
void print() {
for (int i=0;i<=size;++i) {
printf("node %d: ",i);
for (int j=0;j<6;++j) {
int o=stateTable[i].next[j];
if (o) printf("(node #%d: val=%d tag=%d fail=%d) ",o,j+1,stateTable[o].tag,stateTable[o].fail);
}
putchar('\n');
}
puts("next:");
for (int i=0;i<size;++i) {
printf("node %d: ",i);
for (int j=0;j<6;++j)
printf("%d%c",stateTable[i].next[j],j==5?'\n':' ');
}
}
} aho;
struct Gauss {
double a[maxn][maxn];
int sz;
void build(Aho &aho) {
memset(a,0,sizeof a);
sz=aho.size+1;
a[0][sz]=-1;
for (int i=0;i<sz;++i)
a[i][i]=-1;
for (int i=0;i<sz;++i)
if (!aho.stateTable[i].tag)
for (int j=0;j<6;++j) {
int t=aho.getnext(i,j);
a[t][i]+=1.0/6;
}
}
void swap_row(int i,int j) {
for (int k=0;k<=sz;++k)
swap(a[i][k],a[j][k]);
}
void solve() {
for (int i=0;i<sz;++i) {
int pivot=i;
for (int j=i+1;j<sz;++j)
if (fabs(a[j][i])>fabs(a[pivot][i]))
pivot=j;
swap_row(i,pivot);
if (fabs(a[i][i])<1e-12)
return;
for (int j=i+1;j<=sz;++j)
a[i][j]/=a[i][i];
for (int j=0;j<sz;++j)
if (i!=j)
for (int k=i+1;k<=sz;++k)
a[j][k]-=a[j][i]*a[i][k];
}
}
void print() {
for (int i=1;i<=n;++i)
printf("%f%c",a[id[i]][sz],i==n?'\n':' ');
}
void debug() {
for (int i=0;i<sz;++i)
for (int j=0;j<=sz;++j)
printf("%.3f%c",a[i][j],j==sz?'\n':' ');
}
} gauss;
int main()
{
scanf("%d",&T);
while (T--) {
scanf("%d%d",&n,&L);
aho.init();
for (int i=0;i<n;++i) {
for (int j=0;j<L;++j)
scanf("%d",&a[j]);
aho.insert(a,i+1);
}
aho.build();
gauss.build(aho);
gauss.solve();
gauss.print();
}
return 0;
}