您的位置:首页 > Web前端 > JavaScript

BZOJ1444: [Jsoi2009]有趣的游戏 矩阵求逆+AC自动机

2017-01-06 17:01 267 查看
题意:N个人,每个人有一个长度为L的字符串,字符都在前M个大写字母中,现开始连续地随机产生字符,每次产生某个字符的概率是固定的,当一个人的字符串被产生出来他就赢了,求每个人赢的概率。

N,M,L<=10

百度了一下没查着和我一样用矩阵求逆做的。。。好像精度要求不高,邻接矩阵自乘几次就能过?

算了。。。首先赢不了的人可以特判掉,所有人都赢不了就直接退出(我觉得这样应该可以防止矩阵不可逆),建一下AC自动机,就可以把邻接矩阵搞出来,注意单词结点没有任何出边(这题由于所有人单词长度都一样,不存在一个单词结点可能被另一个结点的fail指针指向的情况)

我们要求的每个人获胜的概率等于转移一次获胜的概率+转移两次获胜的概率+……+转移无穷次获胜的概率。设T为初始的邻接矩阵,X为每次转移后的矩阵之和,即:

X=T^1+T^2+T^3+⋯+T^∞

TX=T^2+T^3+⋯+T^∞

(1-T)X=T

所以用T乘以(1-T)的逆矩阵即可(1为单位矩阵),最后X[0]
就是从0号节点(根)转移到N号结点的概率。

矩阵求逆的过程就是和高斯消元解线性方程组一样,不过未知数不是N个数字,而是N个行向量,然后把原矩阵放在左面,单位矩阵放在右面,左边原矩阵消成单位矩阵之后右边的单位矩阵就会变成原矩阵的逆矩阵。

话说我不会严格证明这题里的矩阵一定可逆。。。如果有路过的神犇明白的话欢迎赐教。

#include<cstdio>
#include<queue>
#include<cmath>
#include<algorithm>
#define gm 101
using namespace std;
int maxn;
int n,m,l;
inline void __swap(double *a,double *b)
{
int tms=maxn;
while(tms--)
iter_swap(a++,b++);
}
struct martix
{
double m[gm][gm];
martix():m(){}
martix(int):m()
{
for(int i=0;i<=maxn;++i)
m[i][i]=1;
}
inline double* operator [] (size_t x) {return m[x];}
inline const double* operator [] (size_t x) const {return m[x];}
inline martix operator - (const martix& b) const
{
martix res;
for(int i=0;i<=maxn;++i)
for(int j=0;j<=maxn;++j)
res[i][j]=m[i][j]-b[i][j];
return res;
}
inline martix operator * (const martix &b) const
{
martix res;
for(int i=0;i<=maxn;++i)
for(int j=0;j<=maxn;++j)
for(int k=0;k<=maxn;++k)
res[i][j]+=m[i][k]*b[k][j];
return res;
}
inline void inv()
{
martix res(1);
for(int i=0;i<=maxn;++i)
{
for(int j=i;j<=maxn;++j)
if(fabs(m[j][i])>1e-6)
{
if(i!=j) __swap(m[i],m[j]),__swap(res[i],res[j]);
break;
}
for(int j=i+1;j<=maxn;++j)
{
double temp=m[j][i]/m[i][i];
for(int k=i;k<=maxn;++k)
m[j][k]-=m[i][k]*temp;
for(int k=0;k<=maxn;++k)
res[j][k]-=res[i][k]*temp;
}
}
for(int i=maxn;i>=0;--i)
{
for(int j=0;j<=maxn;++j)
res[i][j]/=m[i][i];
for(int j=i-1;j>=0;--j)
{
for(int k=0;k<=maxn;++k)
res[j][k]-=res[i][k]*m[j][i];
}
}
*this=res;
}
inline martix operator / (martix &b) const
{
b.inv();
return operator*(b);
}
}t,u;
double ps[26];
struct node
{
node *s[26],*fail;
bool da;
node():s(),fail(),da(){}
inline void* operator new(size_t);
}pool[gm];
inline void* node::operator new(size_t)
{
static int t=-1;
return pool+ ++t;
}
struct acam
{
node *rt;
acam():rt(new node){rt->fail=rt;}
inline void insert(const char *s,int& pos)
{
node *now=
4000
rt;
while(*s)
{
node*& x=now->s[*s-'A'];
if(!x) x=new node;
now=x;
++s;
}
now->da=1;
pos=now-pool;
}
inline void aho_corasick()
{
queue<node*> q;
maxn=0;
for(int i=0;i<m;++i)
{
node*& x=rt->s[i];
if(!x) x=rt;
else x->fail=rt,q.push(x);
}
while(!q.empty())
{
++maxn;
node *now=q.front();q.pop();
for(int i=0;i<m;++i)
{
node*& x=now->s[i];
if(!x) x=now->fail->s[i];
else x->fail=now->fail->s[i],q.push(x);
}
}
}
}ac;
int p,q,pos[11];
bool lose[11];
char s[11];
int main()
{
scanf("%d%d%d",&n,&l,&m);
for(int i=0;i<m;++i)
{
scanf("%d%d",&p,&q);
ps[i]=double(p)/q;
}
for(int i=0;i<n;++i)
{
scanf("%s",s);
for(int j=0;s[j];++j)
if(fabs(ps[s[j]-'A'])<1e-6)
{
lose[i]=1;
break;
}
if(lose[i]) continue;
ac.insert(s,pos[i]);
}
bool skip=1;
for(int i=0;i<n;++i)
{
if(!lose[i])
{
skip=0;
break;
}
}
if(skip) goto calc;
ac.aho_corasick();
for(int i=0;i<=maxn;++i)
if(!pool[i].da)
{
for(int j=0;j<m;++j)
t[i][pool[i].s[j]-pool]+=ps[j];
}
u=martix(1)-t;
t=t/u;
calc:
for(int i=0;i<n;++i)
{
if(lose[i]) puts("0.00");
else printf("%.2lf\n",t[0][pos[i]]);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: