1444: [Jsoi2009]有趣的游戏
Time Limit: 10 Sec Memory Limit: 64 MBSubmit: 986 Solved: 325
[ Submit][ Status][ Discuss]
Description
Input
注意 是0<=P
Output
Sample Input
![](http://www.lydsy.com/JudgeOnline/images/1444_4.jpg)
Sample Output
![](http://www.lydsy.com/JudgeOnline/images/1444_5.jpg)
HINT
30%的数据保证, n ≤ 2. 50%的数据保证, n ≤ 5. 100%的数据保证, n , l, m≤ 10.
Source
很容易发现,状态转移出现了环,而且状态不好表示。然后我就不会了...
看了题解才会:
做法是,用ac自动机的next来连接有效状态(只对于字典树中n个人的字母序列中的某个字母),将n个人的“句子”构建成ac自动机,游戏时发生的就是在一个有概率文章中找句子。
之后概率方程是根据自动机中结点间的关系建立的。
nnode为矩阵的行col,nnode+1=var为矩阵的列。
某一行i代表某个结点i是如何从其他节点j转移而来的。
根结点是0。
关于如何构建方程,具体最好是看代码:
根结点很特殊。
#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
#define all(x) (x).begin(), (x).end()
#define for0(a, n) for (int (a) = 0; (a) < (n); (a)++)
#define for1(a, n) for (int (a) = 1; (a) <= (n); (a)++)
#define mes(a,x,s) memset(a,x,(s)*sizeof a[0])
#define mem(a,x) memset(a,x,sizeof a)
#define ysk(x) (1<<(x))
typedef long long ll;
typedef pair<int, int> pii;
const int INF =0x3f3f3f3f;
const int maxn= 100 ;
const double eps= 1e-10 ;
int n,L,m;
double q[30];
int id[maxn+20];
struct Trie//字典树,ac自动机
{
int nex[maxn+20][26],fail[maxn+20],end[maxn+20];
int root,nnode;
int newnode()
{
for0(i,m) nex[nnode][i]=-1;
end[nnode++]=0;
return nnode-1;
}
void init()
{
nnode=0;
root=newnode();
}
void insert(string & buf,int index)
{
int len=buf.length();
int now=root;
for0(i,len)
{
int y=buf[i]-'A';
if(nex[now][y]==-1) nex[now][y]=newnode();
now=nex[now][y ];
}
end[now]++;
id[index]=now;
}
void build()//构建AC自动机
{
queue<int>Q;
fail[root]=root;
for0(i,m)
{
if(nex[root][i]==-1) nex[root][i]=root;
else
{
fail[nex[root][i]]=root;
Q.push(nex[root][i]);
}
}
while(!Q.empty())
{
int now=Q.front();Q.pop();
for0(i,m)
{
if(nex[now][i]==-1) nex[now][i]=nex[fail[now]][i];
else
{
fail[nex[now][i] ]=nex[fail[now]][i];
Q.push(nex[now][i]);
}
}
}
}
}trie;
struct Guass
{
int equ,var;
double a[maxn+20][maxn+20],x[maxn+10];
int elimination()//高斯消元
{
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] );
}
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)
{
for(j=col+1;j<=var;j++) a[i][j]-=a[k][j]*a[i][col];
a[i][col]=0;
}
}
for(i=0;i<equ;i++)
{
x[i]=a[i][var];
}
return 1;
}
void init(int equ,int var)
{
this->equ=equ;
this->var=var;
mem(a,0);
for0(i,equ) a[i][i]=-1;
a[0][0]=a[0][var]=-1;
for(int i=0;i<trie.nnode;i++)
{
if(trie.end[i]) continue;
for0(j,m)
{
if(~trie.nex[i][j])
{
int y=trie.nex[i][j];
a[y][i]+=q[j];//同一个点到另一个点,有多种字母可以到达。
}
}
}
}
}guass;
int main()
{
std::ios::sync_with_stdio(false);
while(cin>>n>>L>>m)
{
double x,y;
for0(i,m)
{
cin>>x>>y;
q[i]=x/y;
}
trie.init();
string s;
for0(i,n)
{
cin>>s;
trie.insert(s,i);
}
trie.build();
guass.init(trie.nnode,trie.nnode);
guass.elimination();
for0(i,n)
{
if(x<eps) puts("0.00");
else printf("%.2f\n",guass.x[id[i]]);
}
}
return 0;
}
/*
3 2 2
1 2
1 2
AB
BA
AA
3 4 2
1 2
1 2
AABA
ABAA
BAAA
*/